Skip to content

fix(server): declare tool/respond route-local failures, harden errors() helper - #1104

Merged
Astro-Han merged 1 commit into
devfrom
claude/i936-toolrespond-contract
Jun 3, 2026
Merged

fix(server): declare tool/respond route-local failures, harden errors() helper#1104
Astro-Han merged 1 commit into
devfrom
claude/i936-toolrespond-contract

Conversation

@Astro-Han

Copy link
Copy Markdown
Owner

Root cause

The errors() response helper silently dropped any status code not present in its ERRORS map. It built { [code]: ERRORS[code] } with ERRORS[code] undefined, and JSON.stringify omits undefined, so the declaration vanished from the generated OpenAPI.

POST /session/:sessionID/tool/respond declared errors(400, 404, 409, 422), so:

  • its 409 and 422 were no-ops (never appeared in the contract), and
  • its 404 was declared with the shared NotFoundError schema even though the handler returns route-local bodies ({ error: "no_pending_tool_call" }, { error: "already_resolved" }, { error, details }).

Change

  • Harden errors(): tighten the signature to (keyof typeof ERRORS)[] so an unregistered status is a typecheck error at the call site, and throw at runtime as a defensive guard for casted/JS callers — instead of emitting an empty declaration. As ERRORS grows (e.g. when 409 is added for busy conflicts), the accepted set grows with it.
  • Fix tool/respond: keep errors(400) for the zod-validator bad-request, and declare 404 / 409 / 422 inline with a route-local ToolRespondFailure schema ({ error: string, details?: unknown }) that matches what the handler actually returns. This also corrects the pre-existing 404 schema mismatch.

Runtime behavior is unchanged — this only corrects the published contract. The handler already returned these bodies and statuses (covered by the existing tool-respond-route runtime tests); now the contract documents them accurately.

On the SDK snapshot

Per this line's strategy, the hand-maintained packages/sdk/openapi.json snapshot and generated SDK are not regenerated here (it is ~24 operations behind the live code and has no committed generator; batch-resynced separately). This PR changes only the live served OpenAPI source.

Verification

  • bun test test/server/error.test.ts test/server/tool-respond-route.test.ts — new cases assert errors() throws on an unregistered code, and that the tool/respond OpenAPI declares 404/409/422 as ToolRespondFailure rather than NotFoundError; 10/10 pass.
  • tsgo --noEmit clean (the tightened errors() signature type-checks all ~70 existing call sites).
  • route-inventory harness green.

Context

#936 Effect error-contract line, contract-completion phase. Enabler/cleanup that unblocks the upcoming busy-conflict 409 slice (which will register 409 in ERRORS) without that change retroactively mis-declaring tool/respond's 409. Code-only slice, no SDK regen.

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@Astro-Han, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 9 minutes and 20 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: adab239a-4372-4bff-b6af-ec1c9e94c1d7

📥 Commits

Reviewing files that changed from the base of the PR and between ddb51fd and d3ba4e2.

📒 Files selected for processing (4)
  • packages/opencode/src/server/error.ts
  • packages/opencode/src/server/instance/session.ts
  • packages/opencode/test/server/error.test.ts
  • packages/opencode/test/server/tool-respond-route.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/i936-toolrespond-contract

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Astro-Han Astro-Han added bug Something isn't working harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority labels Jun 3, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested priority: P2 (includes non-doc, non-test paths outside the low-risk bucket).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

…() helper

The errors() response helper silently dropped any status code not present
in its ERRORS map: it built `{ code: ERRORS[code] }` with ERRORS[code]
undefined, and JSON.stringify omits undefined, so the declaration vanished
from the generated OpenAPI. POST /session/:sessionID/tool/respond used
errors(400, 404, 409, 422), so its 409 and 422 were no-ops, and its 404
was declared with the shared NotFoundError schema even though the handler
returns route-local bodies ({ error } / { error, details }).

- Harden errors(): tighten the signature to (keyof typeof ERRORS)[] so an
  unregistered code is a typecheck error at the call site, and throw at
  runtime as a defensive guard for casted/JS callers, instead of emitting
  an empty declaration.
- Fix tool/respond: keep errors(400) for the zod-validator bad-request,
  and declare 404 / 409 / 422 inline with a route-local ToolRespondFailure
  schema ({ error: string, details?: unknown }) that matches what the
  handler actually returns. This also corrects the pre-existing 404
  schema mismatch.

Runtime behavior is unchanged; this only corrects the published contract.
Per the line's strategy the stale hand-maintained packages/sdk/openapi.json
snapshot and generated SDK are not regenerated here.

Verify: bun test test/server/error.test.ts test/server/tool-respond-route.test.ts
(new cases assert errors() throws on an unregistered code, and that the
tool/respond OpenAPI declares 404/409/422 as ToolRespondFailure rather
than NotFoundError); tsgo --noEmit clean; route-inventory harness green.
@Astro-Han
Astro-Han force-pushed the claude/i936-toolrespond-contract branch from 64a4cd9 to d3ba4e2 Compare June 3, 2026 03:38
@Astro-Han
Astro-Han merged commit 1b6ba1e into dev Jun 3, 2026
33 checks passed
@Astro-Han
Astro-Han deleted the claude/i936-toolrespond-contract branch June 3, 2026 03:52
Astro-Han added a commit that referenced this pull request Jun 3, 2026
…1112)

* chore(sdk): regenerate v2 SDK types for the migrated error contracts

Regenerate packages/sdk/js/src/v2/gen from the current server contract
(`cd packages/sdk/js && bun run build`). This closes the SDK-lag P2 that the
#936 contract slices kept surfacing: the generated v2 SDK error types now match
the live server's declared 4xx responses.

Picked up:
- SessionForkErrors (400, 404) and the fork method's error generic — #1101.
- SessionToolRespondErrors now declares the inline 404/409/422 bodies
  ({ error, details? }) instead of only 400/404 — #1104.
- 409 Conflict (UnknownError) on the nine busy-guarded session routes
  (turnChangeUndo/Redo, turnChangesAggregateUndo/Redo, summarize, deleteMessage,
  shell, revert, unrevert) — #1106.

The regen also absorbs minor pre-existing ordering/description drift from earlier
merges (an EventSessionCompacted union reordering, a VCS diffRaw doc string),
which is just the canonical generator output catching up.

Scope note: this regenerates only the consumed SDK artifact (src/v2/gen). The
checked-in packages/sdk/openapi.json reporting snapshot is intentionally left to
a separate change — it is read only by the route-inventory harness, whose tests
are built to detect (and tolerate) that snapshot's lag, so resyncing it there
would fight that harness by design.

All changes are additive (new error types on existing methods), so SDK consumers
are unaffected. `bun tsc` (run by the SDK build) and the route-inventory harness
tests pass.

Refs #936

* chore(ci): route packages/sdk changes to the harness label

This SDK regen is the first packages/sdk-only PR, which surfaced that the
labeler has no routing glob for packages/sdk. With sync-labels enabled, a
SDK-only PR therefore gets no primary routing label (and any manually added one
is stripped), so the pr-triage label policy ("at least one routing label")
cannot be satisfied.

Route packages/sdk/** to the harness area: the SDK is the opencode server's
generated client, part of the same contract surface as packages/opencode. Lock
the routing in the pr-triage workflow test.

Refs #936
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant