Skip to content

handleRequest() CancellationException catch shadows stopReason:cancelled on session/cancel #91

Description

@fscarponi

handleRequest() CE catch shadows stopReason:cancelled on session/cancel

When session/cancel is received, the SDK sends a JSON-RPC error -32800 ("StandaloneCoroutine was cancelled") instead of the expected {"stopReason":"cancelled"} response.
This causes IDE clients to remain stuck in a "thinking" state after cancellation or catchAgain this pattern Client side

Expected behavior

After session/cancel, the session/prompt response should be:

{"id":5,"result":{"stopReason":"cancelled"}}

Actual behavior

The response is:

{"id":5,"error":{"code":-32800,"message":"StandaloneCoroutine was cancelled"}}

Root cause

In Agent.kt, SessionWrapper.cancel() does two things sequentially:

suspend fun cancel() {
    agentSession.cancel()                                        // step 1
    protocol.cancelPendingIncomingRequest(activePrompt.requestId) // step 2
}

Step 2 cancels the Job that runs the prompt handler. The CancellationException is caught by two competing handlers:

  1. Protocol.handleRequest() (low-level) — catches CE and sends -32800
  2. SessionWrapper.prompt() (high-level) — catches CE and returns
    PromptResponse(StopReason.CANCELLED)

Handler 1 always wins because it wraps the entire handler execution.
Handler 2 is effectively dead code for the cancel path — the CE never
reaches it because handleRequest() catches it first.

Reproduction

  1. Start a session/prompt that triggers a long-running tool (e.g., a build command)
  2. Send session/cancel while the tool is executing
  3. Observe the prompt response: it's -32800 instead of stopReason:cancelled

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions