Skip to content

Data query: a dotted projection entry silently WIDENS the response — fields:["name","account.name"] returns every field #7532

Description

@huangyiirene

Symptom

POST /api/v1/data/showcase_invoice/query with {"fields":["name","account.name"]} answers 200 returning every business field of each record, and no resolved account.name.

Expected: the projection narrows to the named columns (with the dotted path resolved), or — if a dotted path is not supported on this door — a 400 INVALID_FIELD.

Measured across three doors and two object pairs in the same run:

requestobservedexpected
POST /data/showcase_invoice/query {"fields":["name","account.name"]}200, every field, no resolved account.namenarrowed projection
GET /data/showcase_invoice?$select=name,account.name200, every fieldnarrowed projection
POST /data/showcase_task/query {"fields":["title","project.name"]}200, every fieldnarrowed projection
control — POST /data/showcase_task/query {"fields":["title"]}200, correctly narrowed
a nonexistent dotted column200 + every field (swallowed)400 INVALID_FIELD
a nonexistent plain column400 INVALID_FIELD

Two things are wrong at once: the request is answered with strictly more data than it asked for (a projection is how a caller keeps heavy or unwanted columns out of a response, and nothing in the body says it did not apply), and the two spellings of the same mistake — plain vs dotted unknown column — get opposite verdicts on one endpoint. The 400 INVALID_FIELD message the plain spelling produces names this exact failure mode.

Root cause

assertProjectionFieldsExist (packages/metadata-protocol/src/protocol.ts) validates only f.split('.')[0], so a dotted entry passes the ingress gate on its head segment and then defeats the projection downstream — the unknown projection name is dropped and the projection falls back to * (the engine's documented per-axis tolerance for internal callers), i.e. over-return instead of refusal.

Neighbour: #4226 (closed/completed) is the card that closed the select / sort / expand axes against silently-dropped unknown fields. Plain names behave per #4226 in this run; the dotted spelling is the leg its gate does not cover, so treat this as an uncovered leg of — or a regression against — #4226.

Reproduction

  1. Boot showcase on a fresh isolated file DB (SqlDriver / better-sqlite3); authenticate as admin@objectos.ai.
  2. POST /api/v1/data/showcase_invoice/query body {"fields":["name","account.name"]}200, every business field, no resolved account.name.
  3. Same via GET /api/v1/data/showcase_invoice?$select=name,account.name.
  4. Second object pair: POST /api/v1/data/showcase_task/query body {"fields":["title","project.name"]}.
  5. Controls: {"fields":["title"]} narrows correctly; a nonexistent plain column answers 400 INVALID_FIELD; a nonexistent dotted column answers 200 + every field.

Source

Extracted from the QA run #7463 (framework a86db17).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions