Graphify-Labs/graphify · error · RuntimeError
claude -p returned a JSON array with no result object; first
Error message
claude -p returned a JSON array with no result object; first 500 chars of stdout: {stdout[:500]!r} What it means
Raised by `_claude_cli_envelope` when stdout parsed as a JSON array of streamed events but no element has `"type": "result"` and the last element is not a usable dict. The newer CLI emits [system init, assistant turns, ... , result]; if the run was interrupted or the CLI changed shape, the terminal result object is missing and graphify refuses to guess which event carries the answer.
Source
Thrown at graphify/llm.py:1378
carries `result`, `usage`, `modelUsage`, and `stop_reason`.
"""
try:
envelope = json.loads(stdout)
except json.JSONDecodeError as exc:
raise RuntimeError(
f"claude -p produced unparseable JSON envelope: {exc}; "
f"first 500 chars of stdout: {stdout[:500]!r}"
) from exc
if isinstance(envelope, list):
result_events = [
e for e in envelope
if isinstance(e, dict) and e.get("type") == "result"
]
if result_events:
return result_events[-1]
if envelope and isinstance(envelope[-1], dict):
return envelope[-1]
raise RuntimeError(
"claude -p returned a JSON array with no result object; "
f"first 500 chars of stdout: {stdout[:500]!r}"
)
return envelope
def _claude_cli_error(stdout: str) -> str:
"""Return the CLI's own error text when the envelope flags `is_error`.
`claude -p` reports API failures (rate limits, auth) in the stdout JSON
envelope with `is_error: true` and leaves stderr EMPTY — and on a rate limit
it still exits 0. So the two obvious checks both miss it: a non-zero exit
printed a bare "exited 1: " with no cause, and a zero exit fed the error
string to the JSON parser, producing an empty graph that `_response_is_hollow`
misread as truncation and adaptive retry then bisected, re-issuing requests
that were still being refused (#2554). Best-effort: unparseable stdout is not
this function's problem, the caller's `_claude_cli_envelope` reports that.
"""View on GitHub (pinned to 7fe58b0b0f)
Solutions
- Re-run the extraction — transient stream interruptions usually succeed on retry.
- Increase GRAPHIFY_API_TIMEOUT / --api-timeout so the CLI isn't killed before emitting the result event.
- Update (or roll back) the Claude Code CLI if the stream shape changed; verify with `claude -p --output-format json "say ok"` that the array ends with a result object.
- Inspect the first 500 chars quoted in the message to see which event types were emitted instead.
Defensive patterns
Strategy: retry
Try / catch
attempts = 2
for i in range(attempts):
try:
result = extract_files_direct(chunk, root, backend="claude-cli")
break
except RuntimeError as e:
if "no result object" not in str(e) or i == attempts - 1:
raise
# stream was cut (timeout) — retry Prevention
- Set GRAPHIFY_API_TIMEOUT generously for claude-cli so the subprocess isn't killed before the result event flushes.
- Smoke-test `claude -p --output-format json` after every CLI update before running bulk extraction.
When it happens
Trigger: `claude -p` streaming-format output where the final `result` event never arrived: process killed mid-stream (timeout), CLI bug/abort after assistant turns, or a future format that ends with a different event type than the last-element fallback handles.
Common situations: Subprocess timeout (`_resolve_api_timeout()`) cutting the CLI off before the result event; rate-limit or error events terminating the stream abnormally; early-adopted CLI pre-releases with changed event schemas.
Related errors
- claude -p produced unparseable JSON envelope: {exc}; first 5
- Claude Code CLI not found on $PATH. Install from https://cla
- claude -p exited {proc.returncode}: {detail[:500]}
- label response is not parseable JSON: {text[:120]!r}
- tree-sitter is not installed. Run: pip install 'tree-sitter>
AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14).
Data as JSON: /api/errors/afa43318739ab9b2.
Report an issue: GitHub.