From 15f19b1411a264be258a2d58abe8dd993fb3b6f2 Mon Sep 17 00:00:00 2001 From: lukeyoung100 Date: Fri, 13 Mar 2026 10:52:35 -0700 Subject: [PATCH] Fail faster when json.loads is doomed Context: I had an issue in my local setup where I added a hook which outputs a \a character with claude's output to trigger a bell when claude is finished answering a question e.g.: 19 "hooks": [ 20 { 21 "type": "command", 22 - "command": "printf '\\a' >&2; echo -ne '\\a' > /dev/tty 2>/dev/null; echo -ne '\\a' > /proc/$PPID/fd/1 2>/dev/null" 23 } 24 ] This triggered a failure scenario where my myclaw daemon was attempting to talk to claude in json format but instead of line 557 running json.loads('{key:value...etc}') it was failing with json.loads('\a{key:value}'). This caused the daemon to hang for 10 minutes. When I add the exception here it causes the claude sdk to give up way faster. I no longer experienced 10 minute hangs when interacting with the daemon through gchat. Instead the error message shows up in the logs and I experienced only slightly delayed messages. --- .../_internal/transport/subprocess_cli.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py index 1f0aac58..ecaca8b2 100644 --- a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py +++ b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py @@ -561,6 +561,21 @@ async def _read_messages_impl(self) -> AsyncIterator[dict[str, Any]]: # We are speculatively decoding the buffer until we get # a full JSON object. If there is an actual issue, we # raise an error after exceeding the configured limit. + # However, if the buffer doesn't start with '{', no + # amount of additional data will make it valid JSON — + # fail immediately instead of hanging forever. + if json_buffer and not json_buffer.startswith("{"): + bad_prefix = json_buffer[:80] + json_buffer = "" + raise SDKJSONDecodeError( + f"Malformed JSON message: buffer does not start with '{{' " + f"(starts with: {bad_prefix!r}). " + f"Non-JSON data is being written to the CLI's stdout pipe. " + f"Check that hooks and scripts in ~/.claude/settings.json " + f"do not write to stdout — use stderr instead (>&2).", + ValueError(f"Unexpected data on stdout: {bad_prefix!r}"), + ) + continue except anyio.ClosedResourceError: