Skip to content

Commit c0e2cee

Browse files
yeskunallbenbrandt
andauthored
Surface better error message when Claude Code process exits unexpectedly (#366)
As seen in zed-industries/zed#39563, Claude Agent child processes can die mid-session (crash, signal, OOM, et al). When that happens, the SDK throws errors with internal messages like `"ProcessTransport closed"`, `"Failed to write to process stdin"`, or `"process terminated by signal"`. These would bubble up to the user as-is, which is not helpful. At this point, it’s [not possible to continue the session](zed-industries/zed#39563 (comment)) either (which I also confirmed myself), but that is not obvious from the error the user sees (screenshot on the left). In my testing, I could not get the `Error: ProcessTransport is not ready for writing` error, but you can easily trigger the `SIGTERM` (exit code 143) by simply killing the `claude` child process spawned by the ACP adapter while the agent is streaming a response: <img width="1621" height="54" alt="image" src="https://github.com/user-attachments/assets/29d989f1-7e56-44b3-992d-065218444958" /> In this case, you’d do `kill 74573` to trigger the error. AFAICT, the child process is managed internally by the Claude Agent SDK (inside the `query` object returned by `query()`). The ACP agent only sees it as an `AsyncGenerator`. There’s no `process.pid` or `ChildProcess` handle exposed to check liveness against. The only way to discover the process is dead is when the SDK throws one of these transport-level errors*. \*I got these error strings by asking Opus 4.6 to de-obfuscate (`node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs`) | Before | After | |--------|--------| | <img width="466" height="62" alt="image" src="https://github.com/user-attachments/assets/dc959924-0929-4587-9ddd-4a3873f1fa5d" /> | <img width="462" height="77" alt="image" src="https://github.com/user-attachments/assets/77eff880-28da-489a-b9e3-e9f93abf3c7f" /> | In other words, I think the best we (the ACP adapter) can do here is to grep for these transport errors and surface a _better_ error message to the user, as seen above. --------- Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
1 parent b4f56b1 commit c0e2cee

1 file changed

Lines changed: 21 additions & 0 deletions

File tree

src/acp-agent.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,27 @@ export class ClaudeAcpAgent implements Agent {
711711
}
712712
}
713713
throw new Error("Session did not end in result");
714+
} catch (error) {
715+
if (error instanceof RequestError || !(error instanceof Error)) {
716+
throw error;
717+
}
718+
const message = error.message;
719+
if (
720+
message.includes("ProcessTransport") ||
721+
message.includes("terminated process") ||
722+
message.includes("process exited with") ||
723+
message.includes("process terminated by signal") ||
724+
message.includes("Failed to write to process stdin")
725+
) {
726+
this.logger.error(`Session ${params.sessionId}: Claude Agent process died: ${message}`);
727+
session.input.end();
728+
delete this.sessions[params.sessionId];
729+
throw RequestError.internalError(
730+
undefined,
731+
"The Claude Agent process exited unexpectedly. Please start a new session.",
732+
);
733+
}
734+
throw error;
714735
} finally {
715736
if (!handedOff) {
716737
session.promptRunning = false;

0 commit comments

Comments
 (0)