diff --git a/src/claude_code_sdk/_internal/transport/subprocess_cli.py b/src/claude_code_sdk/_internal/transport/subprocess_cli.py index 564bc94d..bc738518 100644 --- a/src/claude_code_sdk/_internal/transport/subprocess_cli.py +++ b/src/claude_code_sdk/_internal/transport/subprocess_cli.py @@ -3,6 +3,7 @@ import json import os import shutil +import logging from collections.abc import AsyncIterator from pathlib import Path from subprocess import PIPE @@ -17,6 +18,9 @@ from ...types import ClaudeCodeOptions from . import Transport +logger = logging.getLogger(__name__) + +MAX_BUFFER_SIZE = 1 * 1024 * 1024 # 1 MB class SubprocessCLITransport(Transport): """Subprocess transport using Claude Code CLI.""" @@ -169,6 +173,7 @@ async def receive_messages(self) -> AsyncIterator[dict[str, Any]]: raise CLIConnectionError("Not connected") stderr_lines = [] + json_buffer = "" async def read_stderr() -> None: """Read stderr in background.""" @@ -188,12 +193,36 @@ async def read_stderr() -> None: if not line_str: continue + json_buffer += line_str + if len(json_buffer.encode("utf-8")) > MAX_BUFFER_SIZE: + logger.warning("JSON buffer exceeded 1MB limit. Discarding buffered data: %s", json_buffer[:200]) + json_buffer = "" + continue + try: - data = json.loads(line_str) + data = json.loads(json_buffer) yield data + json_buffer = "" + except json.JSONDecodeError as e: - if line_str.startswith("{") or line_str.startswith("["): - raise SDKJSONDecodeError(line_str, e) from e + if json_buffer.startswith("{") or json_buffer.startswith("["): + open_braces = json_buffer.count("{") - json_buffer.count("}") + open_brackets = json_buffer.count("[") - json_buffer.count("]") + + if open_braces == 0 and open_brackets == 0: + logger.warning("Malformed JSON detected: %s", json_buffer[:200]) + raise SDKJSONDecodeError(json_buffer, e) from e + + logger.debug( + "Buffering incomplete JSON... (braces: %d, brackets: %d)", + open_braces, + open_brackets + ) + json_buffer += "\n" + continue + else: + logger.debug("Skipping non-JSON line: %s", json_buffer[:200]) + json_buffer = "" continue except anyio.ClosedResourceError: