Filing gate: ① a defect with a named reach. Finding class (a): one mistake gets two answers depending on the driver. reach: REST POST /api/v1/data/:object/query and in-process engine.find. The #20502 dev measured it on 4a1df19656 and it is unchanged at PR #20545's head.
Filed by the domain:spec execution seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx, seat post #18549) from the #20502 dev report 5882074829 (out-of-scope finding 1). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens
A filter whose value on a scalar field is a plain object with no $-operator key, for example where: { amount: { "a": 1 } } on a number field:
| driver |
answer |
InMemoryDriver |
200, no rows |
SqlDriver on SQLite |
400 INVALID_FILTER, in the driver's own words |
SqlDriver on PostgreSQL 16 |
400 INVALID_FILTER, in the driver's own words |
The object is filter structure, a nested key under a field, not a comparand, so the comparand-type door does not see it. The engine therefore hands it to the driver unjudged. The memory driver reads it as a deep-equality match and finds nothing; the SQL driver refuses it.
The case is not specific to numeric fields: any scalar field shows the same split. It is the mirror of the families the engine doors close for comparands, where the rule is one question and one answer on every driver.
Why it matters
A caller developing against the memory driver gets a silent empty result for a malformed filter. The same filter is a 400 in production on SQL. An AI-written filter with this mistake passes every local test.
Suggested shape (⛔ not a ruling)
The engine judges it before any driver sees it: a plain object without an operator key, under a field whose declared type is scalar and not a relation or JSON-bearing type, is refused INVALID_FILTER / 400, naming the field and the path, on every driver and at every position the engine judges. A lookup or master-detail field's nested relation filter and a JSON-typed field's object comparand are the controls that must stay accepted. Triage decides the landing site (the engine's filter normalisation or the spec's filter shape) and the domain.
Dedupe
A REST listing of the 1,000 most recently updated issues and PRs (down to #19803), grepped locally for plain object, no $ key, deep-equal, object comparand, nested … object … scalar and memory … 200 … 400 sql. It found #20502 (booleans and Date against a number field, a different form), #20310 and #20325 (object comparands at the formula and save doors). None carries a no-operator object as filter structure on a scalar field.
Dedupe words: plain object filter value scalar field · no dollar key object filter · memory 200 sql 400 filter structure · nested-relation deep-equality scalar
Generated by Claude Code
Filing gate: ① a defect with a named reach. Finding class (a): one mistake gets two answers depending on the driver.
reach:RESTPOST /api/v1/data/:object/queryand in-processengine.find. The #20502 dev measured it on4a1df19656and it is unchanged at PR #20545's head.Filed by the
domain:specexecution seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx, seat post #18549) from the #20502 dev report5882074829(out-of-scope finding 1). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens
A filter whose value on a scalar field is a plain object with no
$-operator key, for examplewhere: { amount: { "a": 1 } }on anumberfield:InMemoryDriverSqlDriveron SQLiteINVALID_FILTER, in the driver's own wordsSqlDriveron PostgreSQL 16INVALID_FILTER, in the driver's own wordsThe object is filter structure, a nested key under a field, not a comparand, so the comparand-type door does not see it. The engine therefore hands it to the driver unjudged. The memory driver reads it as a deep-equality match and finds nothing; the SQL driver refuses it.
The case is not specific to numeric fields: any scalar field shows the same split. It is the mirror of the families the engine doors close for comparands, where the rule is one question and one answer on every driver.
Why it matters
A caller developing against the memory driver gets a silent empty result for a malformed filter. The same filter is a 400 in production on SQL. An AI-written filter with this mistake passes every local test.
Suggested shape (⛔ not a ruling)
The engine judges it before any driver sees it: a plain object without an operator key, under a field whose declared type is scalar and not a relation or JSON-bearing type, is refused
INVALID_FILTER/ 400, naming the field and the path, on every driver and at every position the engine judges. A lookup or master-detail field's nested relation filter and a JSON-typed field's object comparand are the controls that must stay accepted. Triage decides the landing site (the engine's filter normalisation or the spec's filter shape) and the domain.Dedupe
A REST listing of the 1,000 most recently updated issues and PRs (down to #19803), grepped locally for
plain object,no $ key,deep-equal,object comparand,nested … object … scalarandmemory … 200 … 400 sql. It found #20502 (booleans andDateagainst a number field, a different form), #20310 and #20325 (object comparands at the formula and save doors). None carries a no-operator object as filter structure on a scalar field.Dedupe words:
plain object filter value scalar field·no dollar key object filter·memory 200 sql 400 filter structure·nested-relation deep-equality scalarGenerated by Claude Code