Category: bare-crash (raw leak) + silent parameter drops + invalid returned Plottable. All repro-backed on master (84be35fb3) with a mocked transport. Low/medium severity batch from the 2026-08 remote-surface amplification probe; filed together because they all live in chain_remote_generic's request/response plumbing.
1. NaN / inf in a filter value leaks a raw requests.exceptions.InvalidJSONError
g.gfql_remote([n({'x': float('nan')})], api_token='tok', dataset_id='ds-1', format='json')
# requests.exceptions.InvalidJSONError: Out of range float values are not JSON compliant: nan
Same for float('inf') and for a NaN inside a predicate (n({'x': gt(float('nan'))})). The local engine accepts these values and returns a definite answer; the remote path dies with a requests exception the caller never imported. Note the contrast: every other non-JSON-able filter value (np.int64, pd.Timestamp, datetime, pd.NA, pd.NaT, bytes, np.bool_) is correctly declined with a typed GFQLTypeError from _filter_dict_to_json's validator — NaN/inf slip through that check and blow up in requests.
Expected: extend the same typed check to non-finite floats (a NaN literal is not valid JSON, so this can never go on the wire), or serialize to a defined null/predicate.
2. output= is silently ignored for non-Let queries
chain_remote.py:230-231 only attaches gfql_output inside the if is_let: branch:
g.gfql_remote([n()], output='foo')
# body -> {"gfql_operations": [...], "gfql_query": {...}, "format": "json", "engine": "pandas"} # no gfql_output
g.gfql_remote({'type':'Let', 'bindings': {...}}, output='foo')
# body -> {..., "gfql_output": "foo", ...}
A user who names an output on a chain (or on a Cypher string that happens to compile to a flat chain — the same source text can compile either way depending on whether it needs bindings) gets no error and no effect. Expected: honor it, or raise a typed error saying output requires a Let/DAG query.
3. gfql_remote_shape() cannot take params (or output) at all
ComputeMixin.gfql_remote_shape and chain_remote_shape omit both parameters (ComputeMixin.py:824-846, chain_remote.py:438-493), while gfql_remote has them. Consequence: a parameterized Cypher query can be executed remotely but not shape-queried remotely.
g.gfql_remote_shape("MATCH (a) WHERE a.x > $cut RETURN a")
# GFQLValidationError: [missing-required-field] Missing Cypher parameter '$cut' ...
The error is typed and clear (good), but there is no way to satisfy it through this entrypoint. Expected: thread params/output through the shape variants.
4. node_col_subset can return a Plottable whose _node binding names a column that no longer exists
chain_remote.py:330 / 404 rebind the returned frames onto self's bindings without checking that the bound id/source/destination columns survived the requested column subset:
# server returns nodes without the bound 'id' column
out = g.gfql_remote([n()], node_col_subset=['x'], format='json')
out._node # 'id'
out._nodes # [{'x': 'a'}] -- no 'id' column
No error at the boundary; the invalid Plottable fails later, somewhere else, with a confusing message. Expected: either drop the stale binding, or raise a typed error naming the missing bound column. (This is exactly the class of check validate_graph_shape() was introduced for in #1951, applied to results rather than inputs.)
Cross-ref #1916.
Category: bare-crash (raw leak) + silent parameter drops + invalid returned Plottable. All repro-backed on master (
84be35fb3) with a mocked transport. Low/medium severity batch from the 2026-08 remote-surface amplification probe; filed together because they all live inchain_remote_generic's request/response plumbing.1. NaN / inf in a filter value leaks a raw
requests.exceptions.InvalidJSONErrorSame for
float('inf')and for a NaN inside a predicate (n({'x': gt(float('nan'))})). The local engine accepts these values and returns a definite answer; the remote path dies with arequestsexception the caller never imported. Note the contrast: every other non-JSON-able filter value (np.int64,pd.Timestamp,datetime,pd.NA,pd.NaT,bytes,np.bool_) is correctly declined with a typedGFQLTypeErrorfrom_filter_dict_to_json's validator — NaN/inf slip through that check and blow up inrequests.Expected: extend the same typed check to non-finite floats (a NaN literal is not valid JSON, so this can never go on the wire), or serialize to a defined null/predicate.
2.
output=is silently ignored for non-Let querieschain_remote.py:230-231only attachesgfql_outputinside theif is_let:branch:A user who names an output on a chain (or on a Cypher string that happens to compile to a flat chain — the same source text can compile either way depending on whether it needs bindings) gets no error and no effect. Expected: honor it, or raise a typed error saying
outputrequires a Let/DAG query.3.
gfql_remote_shape()cannot takeparams(oroutput) at allComputeMixin.gfql_remote_shapeandchain_remote_shapeomit both parameters (ComputeMixin.py:824-846,chain_remote.py:438-493), whilegfql_remotehas them. Consequence: a parameterized Cypher query can be executed remotely but not shape-queried remotely.The error is typed and clear (good), but there is no way to satisfy it through this entrypoint. Expected: thread
params/outputthrough the shape variants.4.
node_col_subsetcan return a Plottable whose_nodebinding names a column that no longer existschain_remote.py:330 / 404rebind the returned frames ontoself's bindings without checking that the bound id/source/destination columns survived the requested column subset:No error at the boundary; the invalid Plottable fails later, somewhere else, with a confusing message. Expected: either drop the stale binding, or raise a typed error naming the missing bound column. (This is exactly the class of check
validate_graph_shape()was introduced for in #1951, applied to results rather than inputs.)Cross-ref #1916.