Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/claude_agent_sdk/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,16 @@ def __init__(
class CLIJSONDecodeError(ClaudeSDKError):
"""Raised when unable to decode JSON from CLI output."""

def __init__(self, line: str, original_error: Exception):
def __init__(self, line: str, original_error: Exception, hint: str | None = None):
self.line = line
self.original_error = original_error
super().__init__(f"Failed to decode JSON: {line[:100]}...")
self.hint = hint
message = f"Failed to decode JSON: {line[:100]}..."
# Appended AFTER the truncated line so actionable guidance survives:
# `line` is cut at 100 chars, so anything folded into it can be lost.
if hint:
message = f"{message} {hint}"
super().__init__(message)


class MessageParseError(ClaudeSDKError):
Expand Down
5 changes: 5 additions & 0 deletions src/claude_agent_sdk/_internal/transport/subprocess_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,11 @@ def guard(length: int) -> None:
ValueError(
f"Buffer size {length} exceeds limit {self._max_buffer_size}"
),
hint=(
"Large tool results (file reads, images, MCP responses) "
"can exceed the default; raise it with "
"ClaudeAgentOptions(max_buffer_size=...)."
),
)

try:
Expand Down
10 changes: 10 additions & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,13 @@ def test_json_decode_error(self):
assert error.line == "{invalid json}"
assert error.original_error == e
assert "Failed to decode JSON" in str(error)
assert error.hint is None

def test_json_decode_error_hint_survives_line_truncation(self):
"""The hint must outlive the 100-char truncation of `line`."""
error = CLIJSONDecodeError("x" * 500, ValueError("boom"), hint="Do the thing.")
assert error.hint == "Do the thing."
assert str(error).endswith("Do the thing.")
# The line is still truncated; the hint is what stays readable.
assert "x" * 100 in str(error)
assert "x" * 101 not in str(error)
3 changes: 3 additions & 0 deletions tests/test_subprocess_buffering.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ async def _test() -> None:
pass

assert f"maximum buffer size of {custom_limit} bytes" in str(exc_info.value)
# The error must name the option that fixes it, so users reach for
# max_buffer_size instead of editing the installed package.
assert "max_buffer_size" in str(exc_info.value)

anyio.run(_test)

Expand Down