mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 12:40:32 +00:00
Fix: propagate max_tokens stop reason instead of throwing internal error (#365)
## Summary
- When the Claude SDK reports `stop_reason: "max_tokens"`, the ACP
adapter now returns `{ stopReason: "max_tokens" }` instead of throwing a
`RequestError.internalError` (JSON-RPC `-32603`)
- Scoped to `success` and `error_during_execution` result subtypes only
— `error_max_turns` / `error_max_budget_usd` still correctly map to
`max_turn_requests`
## Problem
When tokens are exhausted, the SDK sends a `result` message with
`stop_reason: "max_tokens"` and `is_error: true`. The adapter hit the
`is_error` check first and threw an internal error, so ACP clients never
received the `stopReason` field. Downstream tools like
[toad](https://github.com/batrachianai/toad/discussions/176#discussioncomment-15445958)
saw a JSON-RPC exception instead of a clean stop reason.
## Fix
Check `stop_reason === "max_tokens"` before the `is_error` throw in the
`success` and `error_during_execution` branches. Token exhaustion is a
normal stop condition, not an internal error.
## Test plan
- [x] Added 5 unit tests covering:
- `success` + `max_tokens` + `is_error: false` → returns `max_tokens`
- `success` + `max_tokens` + `is_error: true` → returns `max_tokens`
(main bug)
- `error_during_execution` + `max_tokens` + `is_error: true` → returns
`max_tokens`
- `success` + `null` stop_reason → still returns `end_turn` (no
regression)
- `success` + `is_error: true` + non-max_tokens → still throws (no
regression)
- [x] All 100 tests pass
- [x] Build passes
Fixes #235
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -543,12 +543,18 @@ export class ClaudeAcpAgent implements Agent {
|
||||
if (message.result.includes("Please run /login")) {
|
||||
throw RequestError.authRequired();
|
||||
}
|
||||
if (message.stop_reason === "max_tokens") {
|
||||
return { stopReason: "max_tokens", usage };
|
||||
}
|
||||
if (message.is_error) {
|
||||
throw RequestError.internalError(undefined, message.result);
|
||||
}
|
||||
return { stopReason: "end_turn", usage };
|
||||
}
|
||||
case "error_during_execution":
|
||||
if (message.stop_reason === "max_tokens") {
|
||||
return { stopReason: "max_tokens", usage };
|
||||
}
|
||||
if (message.is_error) {
|
||||
throw RequestError.internalError(
|
||||
undefined,
|
||||
|
||||
Reference in New Issue
Block a user