[BUG] Binary tool_result output written as raw bytes into JSONL session files, causing null byte / UTF-8 encoding errors in downstream consumers
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When Claude Code executes a tool whose output contains binary data (e.g. reading a binary file, running an executable that produces binary output), the raw bytes are written directly into the JSONL session file as a plain
JSON string with no encoding step. This embeds null bytes (\x00) and other control characters that are illegal in strict UTF-8 contexts.
Inspecting the .jsonl file on disk reveals entries like:
```json
{
"type": "user",
"message": {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "...",
"content": "����\^@\^@\^@\^B\^A\^@\^@\^G..."
}
]
}
}
The \^@ null bytes and replacement characters (U+FFFD, i.e. EFBFBD) are embedded literally in the JSON string value.
What Should Happen?
Binary tool output should be detected and safely encoded before being written to the JSONL file. For example:
- Base64-encode the content and store it with a type marker, e.g. {"type": "base64", "data": "..."}, or
- Sanitize non-UTF-8-safe bytes (null bytes, lone surrogates, control characters) before writing the JSON line.
This would make session JSONL files safe for all downstream consumers (databases, search indexes, log shippers, etc.) without data loss.
Error Messages/Logs
Error observed when inserting the content into PostgreSQL:
ERROR: invalid byte sequence for encoding "UTF8": 0x00
SQLite accepts it silently (permissive behavior), which masks the issue at the storage layer.
Steps to Reproduce
- Ask Claude Code to run a tool that reads or outputs binary data, e.g.:
- cat /usr/bin/ls (or any system binary)
- Any tool invocation where the shell output contains binary bytes
- After the session, open the JSONL file:
~/.claude/projects//.jsonl
- Find the tool_result entry corresponding to the binary output.
- Inspect the content field — it will contain raw binary bytes embedded as a string, including \x00 null bytes and U+FFFD replacement characters.
Confirmed affected session file (for reference):
- Project: open-source-general-agents / wormhole-eventflow
- Session UUID: de225291-aec8-4626-a0fb-a3dd28191cfe
- Message UUID: fbfce6ea-86c6-4ea5-9e07-912af8a9b414
- First 40 bytes of content (hex): EFBFBD EFBFBD EFBFBD EFBFBD 00 00 00 02 01 00 00 07 00 00 00 03 00 00 40 00...
Claude Model
None
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.87
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
This was discovered while building a tool (vibe-history) that syncs Claude Code session history to PostgreSQL. SQLite accepts the null bytes silently, so the bug only surfaces when writing to a strict UTF-8 store.
The workaround currently in place is a fallback sanitization layer on the consumer side that detects PostgreSQL encoding errors and re-encodes the content using JSON escape sequences. However, the correct fix is at the
source — Claude Code should not write non-UTF-8-safe bytes into JSONL in the first place.
The Mach-O binary section names visible in the data (__TEXT, __DATA, __stubs, __cstring, etc.) confirm this is a real compiled binary that ended up as a tool result content string.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗