Skip to content

docs(error-catalog): VALUE_TOO_LONG / VALUE_TOO_SHORT name the field-level codes that really arrive - #20004

Draft
objectstack-fleet[bot] wants to merge 2 commits into
mainfrom
claude/issue-19879-error-catalog-value-length
Draft

objectstack-fleet[bot] wants to merge 2 commits into
mainfrom
claude/issue-19879-error-catalog-value-length

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #19879
Clause-②: no

What

content/docs/api/error-catalog.mdx, the VALUE_TOO_LONG and VALUE_TOO_SHORT entries only. Both entries gave a live cause and a fix, as if a client could branch on the code. No producer emits either code. Both entries now say so, following the shape the INVALID_FORMAT entry got in #19878: the code is reserved, no route emits it today, and a length miss arrives as a field-level max_length / min_length entry. Each entry now says where that entry rides on each path: 400 VALIDATION_FAILED with fields[] for record writes and Zod-parsed request bodies (top-level on /data, under details through the runtime dispatcher), and 400 SETTINGS_VALIDATION with details.fields[] for a settings write. The fix line names both envelopes. Both entries stay because the enum still declares the codes.

Evidence (measured on origin/main e8f163fc)

  • No producer. git grep -nE 'VALUE_TOO_(LONG|SHORT)' outside tests hits only the enum members packages/spec/src/api/errors.zod.ts:58-59, the baseline rows scripts/error-status-unpinned-baseline.json:27-28, this page, the generated content/docs/references/** pages, and ADR-0114, which records these members as a known wart. A grep for other spellings (VALUE_TOO, TOO_LONG, TOO_SHORT) finds only the unrelated PASSWORD_TOO_SHORT in a plugin-auth test. Positive control: INVALID_FORMAT hits errors.zod.ts:57.
  • Record writes. packages/objectql/src/validation/record-validator.ts:695-699 sends fail('max_length', { maxLength, actual }) and fail('min_length', { minLength, actual }) for BOUNDED_STRING_FIELD_TYPES. buildFieldError puts that object on the wire as fields[].constraint, and the envelope's top-level code is VALIDATION_FAILED (VALIDATION_FAILED_CODE, :195).
  • Zod-parsed request bodies. packages/spec/src/api/zod-issues-to-fields.ts:74-81 maps too_small / too_big to min_length / max_length when the value is not a number, bigint, date, array or set. Numbers and dates map to min_value / max_value, and arrays and sets to min_items / max_items. That is why the page says "a string". The REST routes that use this send code: 'VALIDATION_FAILED' (for example packages/rest/src/rest-server.ts:8960-8963).
  • Settings writes (a different envelope). packages/services/service-settings/src/settings-service.ts:397-400 returns max_length / min_length with constraint { minLength?, maxLength?, actual } for a settings value outside its declared length window. :2145 pushes it into the errors list, and :2167 throws SettingsValidationError (settings-service.types.ts:583-584, code = 'SETTINGS_VALIDATION'). packages/services/service-settings/src/settings-routes.ts:215-217 serves it as sendError(res, 400, 'SETTINGS_VALIDATION', …, { details: { namespace, fields } }), and packages/types/src/response-envelope.ts sendError writes that as { success: false, error: { code, message, details } }. So a settings length miss is top-level SETTINGS_VALIDATION with the entry in error.details.fields[], not VALIDATION_FAILED.
  • Where VALIDATION_FAILED puts the list. On the /data routes it is flat (packages/rest/src/error-response.ts:1152-1160, mapDataError: top-level fields). Through the runtime dispatcher it is nested (packages/runtime/src/dispatcher-plugin.ts:645, validationFailureDetails: details.fields). The page's own VALIDATION_FAILED callout already documents both, so the entries link to it rather than restating it.
  • The field-level spellings are the same on all three paths: max_length / min_length. The envelope differs: VALIDATION_FAILED for records and Zod bodies, SETTINGS_VALIDATION for settings.

Not touched

Changeset

None. This is a docs-only change under content/docs/, and no published package's files[] changes, so it falls under skip-changeset. The PM seat applies the label.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands from the merge-base change set (1 path). All 41 derived commands ran on HEAD 37431df1 and exited 0. The derivation is unchanged from the first head, ab8b19ea. They include pnpm check:doc-authoring, pnpm check:doc-anchors, pnpm check:nul-bytes, pnpm check:error-status-conformance, pnpm check:docs-spec-enumerations and pnpm --filter @objectstack/spec run check:docs. The prerequisite closures (lint / formula / client-react, which pulls in spec) were built under the verify lock first. Reconciliation with --ran and the recorded exit codes: 41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero). No package source changed, so no package tests or typecheck are owed.

Rework (PM review)

The first head said every length miss arrives in VALIDATION_FAILED, which is wrong for settings writes. The second commit, 37431df1, names SETTINGS_VALIDATION + details.fields[] for that path in both the Cause and the Fix lines. The #validation_failed link resolves: check:doc-anchors passes, with 377 fragment links resolved.

Acceptance notes

  • The page's intro counts "52 error codes reachable on the wire", but the page carries 53 code headings, and several of them are reserved codes with no emitter (INVALID_FORMAT, INVALID_REFERENCE, and now these two). Whether that count should include reserved codes is a question for the line PR feat(spec)!: retire CONCURRENT_LIMIT_EXCEEDED from StandardErrorCode #19957 already edits. It is not changed here.

Generated by Claude Code

…level codes that really arrive

No producer emits either top-level code; a length miss is refused inside
VALIDATION_FAILED with fields[].code max_length / min_length (record
validator, settings values, Zod-parsed request bodies). Both entries now
say so and tell clients to branch on VALIDATION_FAILED + fields[].code,
mirroring the INVALID_FORMAT entry.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Sep 24, 2026
…entry

A settings write refuses a length miss as 400 SETTINGS_VALIDATION with the
entry in details.fields[], not VALIDATION_FAILED; record writes and
Zod-parsed bodies answer VALIDATION_FAILED with the entry in fields[]
(top-level on /data, under details via the runtime dispatcher). Both
entries and their Fix lines now name both envelopes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 37431df1c5447e898bff3befe718aa9028ff0698

① Derived judgments

Measured on origin/main e8f163fc. The PR head changes one file, content/docs/api/error-catalog.mdx :215-251: the VALUE_TOO_LONG and VALUE_TOO_SHORT entries only.

  1. "No route emits VALUE_TOO_LONG / VALUE_TOO_SHORT today" — TRUE. Non-test git grep over packages/ and apps/ hits only the enum declaration packages/spec/src/api/errors.zod.ts:58-59. Positive control: 'VALIDATION_FAILED' hits packages/rest/src/error-response.ts:1152,1157,1812.
  2. Record-write length miss is field-level max_length / min_length with constraint carrying maxLength/minLength and actual — TRUE. packages/objectql/src/validation/record-validator.ts:693-699; fail at :611-617 routes to buildFieldError, which ships constraint (:395).
  3. On the /data routes the entry rides top-level in fields[] with 400 VALIDATION_FAILED — TRUE. packages/rest/src/error-response.ts:1152-1160.
  4. A Zod-parsed request body answers 400 VALIDATION_FAILED with max_length / min_length — TRUE. Mapper packages/spec/src/api/zod-issues-to-fields.ts:72-80. All seven /data Zod gates in packages/rest/src/rest-server.ts answer top-level fields. Runtime domain routes go through validationFailure(...) (packages/types/src/validation-failure.ts:76-80). No safeParse site answering another code: was found (control: the seven sites).
  5. Under details when the runtime dispatcher serves it — TRUE. packages/runtime/src/dispatcher-plugin.ts:645 and :790-800; the page's existing VALIDATION_FAILED callout already states error.details.fields[] for this door.
  6. A settings write answers 400 SETTINGS_VALIDATION with the entry in details.fields[] — TRUE. packages/services/service-settings/src/settings-service.ts:397,400, thrown at :2167 (settings-service.types.ts:584); settings-routes.ts:215-217.
  7. Fix lines (branch on VALIDATION_FAILED + fields[].code, or SETTINGS_VALIDATION + details.fields[].code) — TRUE. This follows the page's INVALID_FORMAT / INVALID_REFERENCE pattern.
  8. #validation_failed anchor resolves — TRUE. The heading is at :77, and the head's Check Documentation Links check is success.
  9. No parser reads per-entry Cause/Fix lines — TRUE. scripts/check-error-status-conformance.mjs:54-56 reads only the Quick Reference rows and category headings.

Advisory, not a failing claim: the analytics dataset-selection door (packages/rest/src/analytics-selection-door.ts:148,171, from rest-server.ts:11157) is a Zod-parsed body that answers 400 VALIDATION_FAILED with details: { fields }. The placement sentence is correct for the two doors it names, but it is not exhaustive. The link to the VALIDATION_FAILED entry covers the gap: that entry's callout says placement "is decided by the door".

② Semver level

None (docs-only). One file under content/docs/. No package files[] references content/docs. No changeset, and none is warranted. skip-changeset is the correct disposition, but it is not applied yet: Check Changeset fails at "Require a changeset (or the skip-changeset label)".

③ Boundary flags

  • Governed surfaces: none touched. Governed Surface Queue Guard, the single-writer-path check and the card-claims-branch check are all success.
  • Open PR feat(spec)!: retire CONCURRENT_LIMIT_EXCEEDED from StandardErrorCode #19957 edits :6 (count 52 → 51) and deletes CONCURRENT_LIMIT_EXCEEDED at :470-477. This PR's single hunk, :215-251, touches neither, so there is no textual conflict.
  • Nothing misleads an AI author beyond the advisory in ①.
  • Check-runs at read time (37): Check Changeset failed (label missing). Test Core (1/6), Type Check · workspace, Type Check · consumer gates and Lint & Repo Gates were in progress. Every other check was success or path-skipped.

Implemented-by: claude/issue-19879-error-catalog-value-length
Reviewed-by: session_01VDtqoecgES7ScQYGbFVDRv

VERDICT: PASS

Landing still needs, separately from this verdict: the skip-changeset label, with Check Changeset re-run to green, and the four in-progress checks completing green on this head. Isolated at-tier reviewer, adopted by the domain:devx#1 seat.


Generated by Claude Code

This branch has not been deployed

No deployments
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/s

Projects

None yet

1 participant