Preflight Checklist
What's Wrong?
When running commands that produce output with invalid Unicode characters (specifically unpaired surrogate pairs), Claude Code crashes with an API error. The Bash tool successfully captures the output, but when the next API request is serialized to JSON, the invalid Unicode causes JSON encoding to fail.
The error occurs because JSON requires valid Unicode, and unpaired surrogates (U+D800-U+DBFF without corresponding U+DC00-U+DFFF) are not valid.
This commonly happens with:
- Mutation testing tools (mutmut, Stryker) that produce progress bars/terminal graphics
- Tools that emit partial terminal escape sequences
- Commands that capture binary data or corrupted text
What Should Happen?
Claude Code should sanitize Bash output to ensure valid Unicode before:
- Adding the output to the conversation context, OR
- Serializing the API request payload
Invalid Unicode characters should be replaced with the Unicode replacement character (U+FFFD) or removed entirely. This is standard practice for handling untrusted text input.
Error Messages/Logs
API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"The request body is not valid JSON: no low surrogate in string: line 1 column 280696 (char 280695)"},"request_id":"req_011CWo9ijhAZvXz786YjeMai"}
The error position (280696) indicates the invalid character is somewhere in the full request body, not necessarily at the end of the Bash output.
Steps to Reproduce
-
Run a command that produces output with terminal control sequences or binary data:
source .venv/bin/activate && mutmut run --max-children 4 2>&1 | tail -30
-
The Bash command completes successfully, producing output (in my case: 30,029 characters / ~14,608 tokens)
-
On the next API call, Claude Code crashes with the "no low surrogate in string" JSON error
Why mutmut triggers this:
- mutmut uses terminal escape sequences for progress indicators
- Some sequences may be partially captured or corrupted
- The
| tail -30 captures output that may include incomplete escape sequences
Minimal reproduction (if you can create invalid Unicode):
# This would need a command that outputs actual invalid Unicode
# The key is any output containing bytes like 0xED 0xA0 0x80 (unpaired high surrogate)
Claude Model
None
Is this a regression?
I don't know
Last Working Version
No response
Claude Code Version
2.0.76
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Root Cause Analysis
The issue is that Bash tool output is not sanitized for valid Unicode before being included in the API request. While the Bash tool has a 30KB size limit (BASH_MAX_OUTPUT_LENGTH), there's no character validity check.
Suggested Fix
Add Unicode sanitization in one of these locations:
-
Bash tool result processing (before adding to context):
function sanitizeUnicode(text) {
// Replace unpaired surrogates with replacement character
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, '\uFFFD');
}
-
API request serialization (global safety net):
- Sanitize the entire request body before JSON.stringify()
- This would catch issues from any source, not just Bash
Workaround
Users can work around this by using hooks to sanitize Bash output (PostToolUse on Bash), but this should be handled by Claude Code itself.
Related Context
- The Bash tool already has size limiting (30KB)
- MCP tools have token limiting (25K tokens)
- Task/TaskOutput has no built-in limiting (addressed by user hooks)
- None of these tools sanitize for valid Unicode
Preflight Checklist
What's Wrong?
When running commands that produce output with invalid Unicode characters (specifically unpaired surrogate pairs), Claude Code crashes with an API error. The Bash tool successfully captures the output, but when the next API request is serialized to JSON, the invalid Unicode causes JSON encoding to fail.
The error occurs because JSON requires valid Unicode, and unpaired surrogates (U+D800-U+DBFF without corresponding U+DC00-U+DFFF) are not valid.
This commonly happens with:
What Should Happen?
Claude Code should sanitize Bash output to ensure valid Unicode before:
Invalid Unicode characters should be replaced with the Unicode replacement character (U+FFFD) or removed entirely. This is standard practice for handling untrusted text input.
Error Messages/Logs
API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"The request body is not valid JSON: no low surrogate in string: line 1 column 280696 (char 280695)"},"request_id":"req_011CWo9ijhAZvXz786YjeMai"} The error position (280696) indicates the invalid character is somewhere in the full request body, not necessarily at the end of the Bash output.Steps to Reproduce
Run a command that produces output with terminal control sequences or binary data:
The Bash command completes successfully, producing output (in my case: 30,029 characters / ~14,608 tokens)
On the next API call, Claude Code crashes with the "no low surrogate in string" JSON error
Why mutmut triggers this:
| tail -30captures output that may include incomplete escape sequencesMinimal reproduction (if you can create invalid Unicode):
Claude Model
None
Is this a regression?
I don't know
Last Working Version
No response
Claude Code Version
2.0.76
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Root Cause Analysis
The issue is that Bash tool output is not sanitized for valid Unicode before being included in the API request. While the Bash tool has a 30KB size limit (BASH_MAX_OUTPUT_LENGTH), there's no character validity check.
Suggested Fix
Add Unicode sanitization in one of these locations:
Bash tool result processing (before adding to context):
API request serialization (global safety net):
Workaround
Users can work around this by using hooks to sanitize Bash output (PostToolUse on Bash), but this should be handled by Claude Code itself.
Related Context