Current behavior
When using claude --print --output-format json, the output is wrapped in a CLI envelope:
{"type":"result","result":"```json\n{...}\n```","usage":{"input_tokens":...,"output_tokens":...}}
The result field contains the LLM's raw text output, which itself may include code fences (\``json ... ````), preamble text before the JSON, or other formatting artifacts.
Expected behavior
--output-format json should return the LLM's JSON response directly, parsed and unwrapped:
If the LLM output is not valid JSON, return an error envelope instead of silently embedding broken JSON in a string field.
Impact
Every downstream consumer of claude --print --output-format json must implement three defensive layers:
- Envelope unwrap — detect
{type: "result"} wrapper and extract .result
- Fence stripping — remove
```json ... ``` code fences from the string
- Preamble removal — skip any conversational text before the actual JSON (e.g., "Here is the analysis:\n{...}")
We have 13+ flows in our pipeline that parse Claude output, and each one needs all three protections. Missing any one of them causes silent data loss or crashes. We've had production bugs from incomplete parsing in multiple flows.
A flag like --output-format json should mean "give me JSON I can pipe directly to jq or JSON.parse" — not "give me an envelope containing a string that might contain JSON somewhere inside it."
Suggested fix
When --output-format json is specified:
- Extract the LLM's text response
- Strip code fences and preamble
- Parse as JSON
- Return the parsed object directly (or an error object if parsing fails)
Current behavior
When using
claude --print --output-format json, the output is wrapped in a CLI envelope:{"type":"result","result":"```json\n{...}\n```","usage":{"input_tokens":...,"output_tokens":...}}The
resultfield contains the LLM's raw text output, which itself may include code fences (\``json ... ````), preamble text before the JSON, or other formatting artifacts.Expected behavior
--output-format jsonshould return the LLM's JSON response directly, parsed and unwrapped:{"key": "value", ...}If the LLM output is not valid JSON, return an error envelope instead of silently embedding broken JSON in a string field.
Impact
Every downstream consumer of
claude --print --output-format jsonmust implement three defensive layers:{type: "result"}wrapper and extract.result```json ... ```code fences from the stringWe have 13+ flows in our pipeline that parse Claude output, and each one needs all three protections. Missing any one of them causes silent data loss or crashes. We've had production bugs from incomplete parsing in multiple flows.
A flag like
--output-format jsonshould mean "give me JSON I can pipe directly tojqorJSON.parse" — not "give me an envelope containing a string that might contain JSON somewhere inside it."Suggested fix
When
--output-format jsonis specified: