Skip to content

[BUG] API Error 400 "no low surrogate in string" when Bash output contains invalid Unicode #16294

Description

@coygeek

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

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:

  1. Adding the output to the conversation context, OR
  2. 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

  1. 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
  2. The Bash command completes successfully, producing output (in my case: 30,029 characters / ~14,608 tokens)

  3. 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:

  1. 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');
    }
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions