Skip to content

Align validation error fields and malformed IDs with official responses - #45

Merged
SaladDay merged 6 commits into
mainfrom
codex/validation-error-fields
Sep 23, 2026
Merged

SaladDay merged 6 commits into
mainfrom
codex/validation-error-fields

Conversation

@SaladDay

@SaladDay SaladDay commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Core already rejected these invalid requests, but with generic codes and null params. It also returned 500 for text PostgreSQL cannot store, and returned 400 instead of 404 for malformed resource IDs. This batch aligns the error fields with owned official observations. It does not change any limit or accepted value. The pinned baseline is unchanged (SDK 3.13.0 / d7c41ef / agents=v1).

Behavior

  • Metadata validation on Agents and Sessions:

    • more than 16 pairs: param metadata;
    • key over 64 characters: param metadata.<key>;
    • value over 512 characters: param metadata.<key>;
    • a non-string value on any metadata-accepting operation, including Vault create: param metadata.<key>.

    All use code invalid_request_error with the observed official message format. Vault metadata still has no size limits, as on the official service.

  • Agent name over 128 characters: param name, code invalid_request_error.

  • U+0000 and invalid UTF-8:

    • Metadata keys and values containing U+0000 are rejected explicitly with param metadata.<key>.
    • Any other text that PostgreSQL cannot store or compare (errors 22021/22P05) now returns 400 invalid_request_error instead of 500, and nothing is written.
    • This is a documented local limit: the official service stores U+0000.
  • Malformed path IDs: a malformed (non-UUID) ID now follows exactly the missing-ID path on every Beta, Files and Skills route, so it gets a byte-identical response, including when the body or query is invalid. The ID is resolved to a UUID that Core never assigns (malformed Skill version paths resolve to version 0, which the database forbids). Malformed list cursors and body ID references keep their current behavior.

  • Template network rejections: these (and the shared inline Session network check) use code invalid_request_error with a null param. The accepted hostname forms are unchanged and not widened. Unknown-field errors are now reported before network errors, so the result no longer depends on map order.

Evidence

Campaign scan 1 findings VA-07/08/09/10, SES-28 and SFT-20 (owned official requests, request IDs retained privately), plus the Sept 22 official Session metadata type error. Recorded in contracts/agents-api/official-semantics-alignment.md and operation-evidence.md.

Validation

  • Independent acceptance, written from the requirements only and run against a real Core HTTP server with dedicated PostgreSQL, over raw HTTP plus the pinned SDK 3.13.0: 1,245 checks across 1,468 requests, with 33 tables fingerprinted to prove rejected requests write nothing.
  • Go tests: handler and store tests for every row, including a real-PostgreSQL 46-route path-ID matrix and a U+0000 write-proof test. The repository official-client and environment-network acceptance scripts pass against a built server.
  • Server gate on this head: make -o check-web check plus Web typecheck, core-doctor, 301 client tests, 602 Web tests and the build all pass. The Playwright browser cases were not run on the server (no Google Chrome there; skip approved by the user).
  • Generated files: make openapi is byte-identical. No SQL changes.
  • No live model run: validation happens before execution.
  • Independent blind review of the full diff by a fresh Claude Code subagent (the user-approved replacement for GPT-6 Astra): no blockers. Its follow-ups were applied in the last three commits: a generic message for unstorable text, doc and comment accuracy, and one more route in the path-ID matrix.

Documented differences and deferred items

  • Official abbreviates long metadata keys in the M2 message (a single sample). Core keeps the full key; code and param match.
  • Official stores U+0000 and accepts a disabled network with domains; Core rejects both with 400.
  • Duplicate metadata keys, uppercase and braced ID spellings, SES-33 validation order and hostname widening (SFT-21) are recorded for later work.

No full protocol compatibility is claimed.

A malformed path identifier on Session-family, Environment, Template,
Credential, Agent update and Skill routes returned 400 "Invalid resource
identifier or request limits." where a well-formed missing identifier
returned 404, and some routes rejected the identifier before validating
the body or query. Resolve malformed path identifiers to the maximum
UUID, which Core never assigns, so each request follows exactly the
missing-identifier path, including body, query and storage checks.
Routes whose lookup is the next check keep their direct not-found
response. Malformed list cursors and request-body references keep their
existing errors (SES-28).
Metadata limits and value types, Agent names and Template network
rejections now return 400 invalid_request_error with the observed param
and message instead of local codes with a null param (VA-07/08/09,
SFT-20). A non-string metadata value is reported in document order
before the generic body error; limit checks keep their position, and
Vault metadata still has no pair or length limits.

PostgreSQL text and jsonb cannot store U+0000, which produced 500s
(VA-10). Metadata rejects it explicitly with its metadata.<key> param;
other stored strings map the untranslatable-character errors to 400
invalid_request_error. The failing statement aborts its transaction, so
nothing is written. This is a documented local limit: the official
service accepts U+0000.
Add the dated batch section to the official semantics alignment
document with the row matrix, evidence, design decisions and deferred
items, register the batch in the operation evidence inventory, and
update CONTRIBUTING, the contract README and the list query deferred
list where they described the previous behavior.
PostgreSQL error 22021 also reports invalid UTF-8 in text parameters,
such as a Session list agent_id filter of %ff, so the U+0000-specific
message was wrong there. Keep status 400, code invalid_request_error and
a null param with a message that covers both causes. The explicit
metadata U+0000 errors and their metadata.<key> param are unchanged.
Record that the official service abbreviates long metadata keys in the
M2 message while Core quotes the full key, since one uniform sample
cannot reveal the rule. Limit M4 to Vault create, as neither Core nor
the pinned SDK has a Vault update. Describe duplicate metadata keys
accurately: the pre-decoding scan checks only the last value, while
typed decoding rejects a non-string at any occurrence. No behavior
changes.
Replay POST /v1/skills/{skill_id}/versions with malformed, missing and
foreign Skill IDs, using valid and invalid archives and default fields,
and require byte-identical responses without writes.
@SaladDay
SaladDay force-pushed the codex/validation-error-fields branch from 07a568b to efe5c91 Compare September 23, 2026 09:04
@SaladDay
SaladDay merged commit b710064 into main Sep 23, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant