Skip to content

Commit 4634544

Browse files
docs(error-catalog): INVALID_FIELD names the refusal of a search over an empty resolved search set (#24353)
Fixes #24331 Clause-②: no ## What One sentence is added to the **Cause** text of `### INVALID_FIELD` in `content/docs/api/error-catalog.mdx`, right after the `internal: true` sentence. It names the refusal that PR #24320 (`ec749fbb44`) added. When an object's resolved search set is empty, a non-blank `search` gets `400 INVALID_FIELD` with `param: 'search'`. It no longer gets every row back. The sentence also gives the two remedies the refusal message itself prescribes. No other entry changes and no section is added. The diff is +4 / -0 lines, all inside that entry. ## Read from the code, not from the card The wording comes from the producer as it stands on the base `33f700b87b`. No commit between `ec749fbb44` and the base touches `packages/objectql/src/engine.ts` or `packages/objectql/src/search-filter.ts`: the `git log` over that range and those paths is empty. `git merge-base --is-ancestor ec749fb 33f700b` exits 0. - **Where.** `ObjectQL.expandSearchOnAst` in `packages/objectql/src/engine.ts`. When `expandSearchToFilter` returns `null` and the normalized term is non-blank, it throws `emptySearchSetError(...)`. `find`, `findOne`, `count` and `aggregate` all reach this call. - **Envelope.** `status: 400`, `code: 'INVALID_FIELD'`, `object`, `param: 'search'`. This matches the card. - **When the set is empty.** `resolveSearchFieldResolution` in `packages/spec/src/data/search-fields.ts` runs over the object's fields after the `internal: true` ones are removed. A `searchableFields` entry counts only when it names an existing field that is not a `formula`. If no entry does, the auto-default is used, and the refusal fires only when the auto-default is empty too. The auto-default skips hidden fields, the system names in `SEARCH_AUTO_EXCLUDED_FIELDS` (`id`, `created_at`, `owner_id`, and others), and every type outside the textual set plus `select`. - **Where the code differs from the card.** The card says the set is empty when "the object has no textual or enum stored field and no `searchableFields` entry naming one". The code differs in two ways. Some textual fields do not count: a hidden one, a system-named one, or one marked `internal: true`. And a declared entry may name any stored field, such as a `lookup` or `json` column, not only a textual one. So the sentence says "the default search scans none" and "`internal: true` fields never count", not "no textual or enum stored field". - **Remedy.** The message ends with the two remedies: declare `searchableFields` on the object naming a stored field, or mirror the composed title onto a stored text field and search that. The card's remedies match the message. The sentence keeps both. It says "a composed title" because the catalog sets up no junction object for "the" to refer to. ## Verification - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived the commands from the tree at `d87f5a77f`, the head of this PR. The change set is 1 path and the list has 41 commands, the same list as the dispatch-time reading. - All 41 were run. Each exit code was captured before any pipe, and every final exit is 0. - Six commands first refused with exit 3 (`PREREQUISITE NOT MET`) because packages were not built. Five needed the `@objectstack/lint` closure: `check:doc-formula-expressions`, `check:doc-security-posture`, spec `check:docs`, `check:docs-transcript-drift` and spec `check:skill-examples`. The client SDK case of `check:skill-examples` also needed the client packages built. - The closures were built through `scripts/pm/os-verify-lock.sh`. The `@objectstack/lint...` build was 4/4 tasks, all cache hits. The `@objectstack/client-react...` plus `@objectstack/client...` build was 35/35 tasks, 34 of them cache hits. The six commands were then re-run, and all six exited 0. - The `--ran` reconciliation printed: `41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN`. - Sanity check (not a gate): the edited page compiles with `@mdx-js/mdx` 3.1.1 (exit 0), and the new sentence appears once in the output. - Left to CI: `Build Docs`, `Test Core` and the type-check lanes. The derivation lists these as CI-only. - No package is touched, so no package test or typecheck is owed. There is no changeset because the change is docs only (`skip-changeset`). - `origin/main` has moved to `dd72a047f`, which touches only `.claude/skills/pm-dispatch/**`. That is disjoint from this diff, so main was not merged in. ## Acceptance notes These other hand-written pages describe how `search` behaves on the data read route, and none of them mentions this refusal. They are left unchanged here because they are outside this card's file surface: - `content/docs/protocol/kernel/error-handling.mdx:626`: says the catalog's `INVALID_FIELD` entry "enumerates every read axis this one code answers on". Its list in parentheses names `select`, `expand`, `searchFields`, `groupBy` and `aggregations[].field`, but not `search`. - `content/docs/api/data-api.mdx:26`: the `search` parameter row. Also `content/docs/api/data-api.mdx:153-158`: a table of `search` / `searchFields` requests and their results, with no row for a bare `?search=` on an object that has no searchable field. - `content/docs/data-modeling/queries.mdx:476` (the Full-Text Search section) and `content/docs/protocol/objectql/query-syntax.mdx:1120`: both describe how search fields are resolved and how `searchFields` is refused, but not this refusal. --- _Generated by [Claude Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 69147ab commit 4634544

1 file changed

Lines changed: 4 additions & 0 deletions

File tree

‎content/docs/api/error-catalog.mdx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ aggregation's own `filter`), a sort, a `groupBy`, an `aggregations[].field`, a
159159
`searchFields` entry, or an `expand` entry's own filter or sort at any depth. Such a
160160
field is never returned on a generic read, so it cannot be queried by either — the
161161
refusal names the object that declares it. Query by another field.
162+
A non-blank `search` is refused (`param: 'search'`), not answered with every row,
163+
when the object's resolved search set is empty: no `searchableFields` entry names a
164+
stored field, and the default search scans none (`internal: true` fields never
165+
count). Declare one that does, or mirror a composed title onto a stored text field.
162166
Off the request path the same code answers `backfillSummaryNulls`'s
163167
`recomputeUndefinedOnEmpty` (`os migrate summary-nulls
164168
--recompute-undefined-on-empty object.field`) when an entry is not a roll-up

0 commit comments

Comments
 (0)