fix(server): stopping a Claude turn no longer surfaces a raw stream error - #5162
fix(server): stopping a Claude turn no longer surfaces a raw stream error#5162abcdmku wants to merge 2 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bd3dfb0c5c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ApprovabilityVerdict: Approved 092f68d Straightforward bug fix that prevents raw stream errors from being surfaced when users intentionally stop a Claude turn. The implementation tracks interrupted turns and treats their errors as expected interruptions. Changes are limited in scope with comprehensive test coverage. You can customize Macroscope's approvability policy. Learn more. |
092f68d to
1cb6a8f
Compare
The interrupt bug will be fixed with the new ClaudeAdapterV2 in #2829. Fixes are here and here. Since that PR has been open for awhile I figured I'd open a request for the current ClaudeAdapter:
What Changed
Stopping a Claude turn no longer surfaces a raw runtime error. interruptTurn records the stop request on the session context, and both places that classify how a turn ended (handleStreamExit for stream failures, handleResultMessage for error results like ede_diagnostic) treat what follows as an interruption, completing the turn with a graceful message. The flag resets on turn start and completion. Includes a test for each path.
Why
The adapter inferred interruption by matching error text against known phrases, so any stop producing an unrecognized message was recorded as failed with a runtime error (#4713, #3836). Claude is the only provider with this problem: the other adapters get typed stop signals, while the Claude SDK's interrupt() returns nothing and the turn ends with an arbitrary error. Tracking intent is the approach Grok already uses.
After fix:

Thread before/after:

Verified with
vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts (64 passed),
pnpm run tc,
and a manual test for the photos above
Checklist
Note
Medium Risk
Changes turn-completion and error classification in the legacy Claude adapter on user stop; behavior is scoped to explicit interrupts but affects runtime events the UI relies on.
Overview
When a user stops a Claude turn, the adapter now records intent instead of inferring interruption only from error text.
interruptTurnadds the target turn id tointerruptedTurnIdson the session context before calling the SDKinterrupt().handleResultMessageandhandleStreamExitthen treat a non-success result or stream failure for that turn asinterrupted, completing with a graceful message and not emittingruntime.error. The set is cleared when a turn completes.Tests cover error-result and stream-failure paths after a user stop.
Reviewed by Cursor Bugbot for commit 1cb6a8f. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Fix Claude turn stops to emit
interruptedinstead of surfacing a raw stream errorinterruptedTurnIdsset toClaudeSessionContextto track turns the user explicitly stopped.interruptedrather thanfailed, suppressing theruntime.errorevent.interruptedwith the message'Claude runtime interrupted.'instead of emitting aruntime.error.interruptTurnnow records the target turn ID before callingquery.interrupt(), so subsequent result and stream-exit handlers can detect the interruption correctly.Macroscope summarized 1cb6a8f.