Skip to content

Return official errors for unresolved list cursors - #71

Merged
SaladDay merged 7 commits into
mainfrom
codex/cursor-errors
Sep 23, 2026
Merged

SaladDay merged 7 commits into
mainfrom
codex/cursor-errors

Conversation

@SaladDay

@SaladDay SaladDay commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

When a list's after cursor does not resolve, each list now returns the officially observed error for its family. The pinned baseline is unchanged (SDK 3.13.0 / d7c41ef / agents=v1).

Behavior

Lists Unresolved cursor (random, malformed, other type, other parent, deleted, foreign)
Agents, Sessions, Turns, Templates, Vaults, Credentials 404 not_found_error, param null. A malformed cursor now takes the missing-ID path (it used to be 400 invalid_request)
Session Items, Subagent Items, Subagent Turn Items 400 invalid_request_error, param null, Invalid session item ID in `after`
Subagents, Subagent Turns 400 invalid_request_error, Invalid resource ID in `after`
Artifacts 400 invalid_request_error, after is not a valid artifact ID
Skill versions Not a version ID: 400 invalid_value, param after. Missing: 404. Another Skill's version (same tenant only): 400 invalid_value, "Skill version cursor does not match this skill."
Files, Skills, Environment Files Unchanged; they already matched
  • Parent check first: a missing or foreign parent is still checked first and returns 404. A foreign cursor is byte-identical to a missing one. Artifact and Skill-version lists re-check the parent before returning a 400, so a parent deleted mid-request still gets 404.
  • Bounded echo: the Skill version and Skills order errors echo the caller's value only when it is at most 256 bytes of printable, valid UTF-8 (shared internal/echotext). Otherwise the message omits the value.
  • Unchanged: valid-cursor paging, ordering and limits. The Core-only Runtime observations list shares the Session cursor and now answers a malformed cursor with 404 (documented).
  • Upstream anomalies not copied: the official 500s seen for some cursors. Deleted Agent/Session IDs that still work as official cursors (ERR-15) would need tombstones and are deferred.

Evidence

  • Campaign scan 4 (105 owned official requests, 2 Sessions, 4 Turns, all deleted): ERR-01..06.
  • SAT-04 (scan 3) and HE-57 (scan 2).
  • Recorded in list-query-semantics.md and operation-evidence.md (register CE and the list rows).

Validation

  • Independent acceptance (written from the requirements only; real Core with PostgreSQL; raw HTTP and the pinned SDK; 968 requests with seeded Subagent/Artifact rows and a no-write fingerprint):
    • On main, the 286 changed checks failed and the 1,002 retained, isolation and paging checks passed.
    • On the final head, all rows pass. Foreign cursors are byte-identical to missing ones.
  • Go tests: a real-PostgreSQL public test compares exact bytes for every row for two tenants and pages every list with has_more/first_id/last_id checks. Echo-bound tests cover 257-byte, 200 KB, control-character and invalid-UTF-8 values. The pinned-SDK list-query script passes.
  • Server gate on this head: all make check targets plus the Web typecheck, core-doctor, unit tests and build pass. The Playwright browser cases were not run on the server (no Google Chrome; the user approved the skip). make sqlc-generate and make openapi are byte-identical.
  • Independent blind review by a fresh Claude Code subagent (the user-approved replacement for GPT-6 Astra) found no tenant or parent leak. Its blocker, the unbounded echo, and its follow-ups (docs, stronger paging checks, the Artifact race) are fixed in the last commits. These were verified by the unchanged acceptance script and the gate, without a new review, per the user's rule.

No full protocol compatibility is claimed.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

An after cursor that does not resolve inside its already resolved parent
now answers with the list family's observed error (ERROR-PROTOCOL-001):
Agent, Session, Turn, Template, Vault and Credential cursors take the
missing-cursor 404 path, malformed ones included; Item, Subagent and
Artifact lists return their 400 invalid_request_error messages; Skill
versions report invalid_value on after for a non-version value or another
Skill's version and keep the 404 for a missing one. Parent lookups still
run first and foreign cursors stay identical to missing ones.
official_list_query.py replays cursor rows C1, C2, C5, K1 and K3 through
raw HTTP and the pinned SDK for tenants A and B. The container client,
Agent, Vault, Credential and Item scripts and the Subagent acceptance
script now expect the new cursor errors.
Add the dated list cursor section with evidence and decisions, replace
the malformed-cursor exception in the validation batch, register evidence
P and update the list rows, and refresh stale cursor notes.
The Skill version prefix error and the Skills order error repeat the
caller's value only when it is at most 256 bytes of valid, printable
UTF-8, the rule already used for echoed field names, now shared through
internal/echotext. Longer or unprintable values get a fixed message, so
a large or control-character cursor cannot inflate the response.
Artifact and Skill version lists read the parent and the cursor in
separate statements. Re-check the parent before reporting the cursor
400, so a Session or Skill deleted in between still returns its 404.
Seed at least two resources per list and page each one resource at a
time in both orders, checking the page contents, has_more and the first
and last IDs. Child Turns get distinct creation times for a stable order.
Name base c5cb6b5, describe the bounded echo and the parent re-check,
and update the Runtime observation error table to the current types,
codes and ignored unknown list keys.
@SaladDay
SaladDay merged commit cda7c51 into main Sep 23, 2026
2 of 3 checks passed
@SaladDay
SaladDay deleted the codex/cursor-errors branch October 7, 2026 06:37
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