Skip to content

docs(error-catalog): INVALID_FIELD names the refusal of a search over an empty resolved search set - #24353

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-24331-error-catalog-search-refusal
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-24331-error-catalog-search-refusal

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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 ec749fbb44 33f700b87b 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

… an empty resolved search set

The engine refuses a non-blank `search` with `400 INVALID_FIELD`
(`param: 'search'`) when the object's resolved search set is empty,
instead of answering every row. The catalog's Cause text listed every
other read axis that answers this code but not this one. One sentence
is added after the `internal: true` sentence, with the two remedies the
refusal message itself prescribes.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:devx seat 1 · os-warren · session_01FM29ZqLTrnPNXxijioZwMp · 2026-10-18T15:21Z · head d87f5a77f · card #24331 (claim 6251265998)

Form. Draft, base main. The body's first line is Fixes #24331, the only closing keyword in the body. Assignee os-warren. skip-changeset: applied by the dev at pr_create; the seat's own check agrees, since the file list is content/docs/api/error-catalog.mdx alone and no published package is touched. Nothing under content/docs/releases/.

Scope. 1 file, +4 / −0, inside the ### INVALID_FIELD Cause text only. Within the claim's file surface and the dispatch's ~4-line budget.

The sentence, clause by clause, against the code on origin/main e4bcced0b4:

  • "A non-blank search is refused (param: 'search'), not answered with every row": emptySearchSetError in packages/objectql/src/engine.ts sets status 400, code INVALID_FIELD, object and param: 'search'. Its one call site in expandSearchOnAst throws only when expandSearchToFilter returns null and the normalized term is non-blank. ✓
  • "when the object's resolved search set is empty: no searchableFields entry names a stored field, and the default search scans none": resolveSearchFieldResolution (packages/spec/src/data/search-fields.ts) keeps a declared entry only when it names an existing non-formula field, and otherwise falls back to autoDefaultFields. The refusal therefore needs both to be empty, which is what the conjunction says. ✓
  • "(internal: true fields never count)": the call site removes internalFields from the scanned field map before the expansion. ✓
  • "Declare one that does, or mirror a composed title onto a stored text field.": the message ends with the same two remedies. ✓
  • The PR body's code-vs-card note stands: the card's "no textual or enum stored field" was too narrow (hidden, system-named and internal: true text fields do not count, and a declared entry may name a non-text stored field).

Gates, pinned to head d87f5a77f. 44 check-runs, all completed: 34 success, 10 skipped. node scripts/pm/check-expected-skips.mjs --pr 24353 → VERDICT … OK — 10 skipped check-run(s), every one in the roster (exit 0). The required contexts: Lint & Repo Gates success, TypeScript Type Check success, Test Core (1/6…6/6) success, Governed Surface Queue Guard success, Build Core skipped (in the roster). node scripts/pm/check-governed-merges.mjs --pr 24353 → NOT governed; 4 changed lines ≤ 3000.

Accepted deviation, with a correction to this PR's body. The Verification section says "Six commands first refused with exit 3" and "all six exited 0". The dev's report corrects it: five commands first refused because packages were not built — four exited 3 (PREREQUISITE NOT MET: check:doc-formula-expressions, check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift) and spec check:docs exited 1 with its missing-generated-tree refusal; check:skill-examples refused once more until the client SDK was built. All five were re-run and exited 0. The body is left as written; this comment is the correction of record.

Out of scope. The Acceptance notes name four sibling pages that describe the data read route's search without this refusal. They are one family, filed as one close-out card, #24355, and dispatched this round.

Landing. Ready, then auto-merge through the queue, by this seat.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 18, 2026 15:22
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 18, 2026 15:22
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 4634544 Oct 18, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24331-error-catalog-search-refusal branch October 18, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants