{"record":{"id":"d814b6986357b59e","repo":"Graphify-Labs/graphify","slug":"claude-p-produced-unparseable-json-envelope-exc","errorCode":null,"errorMessage":"claude -p produced unparseable JSON envelope: {exc}; first 500 chars of stdout: {stdout[:500]!r}","messagePattern":"claude -p produced unparseable JSON envelope: (.+?); first 500 chars of stdout: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"graphify/llm.py","lineNumber":1365,"sourceCode":"            file=sys.stderr,\n        )\n        result[\"finish_reason\"] = \"length\"\n    return result\n\n\ndef _claude_cli_envelope(stdout: str) -> dict:\n    \"\"\"Parse the JSON returned by `claude -p --output-format json`.\n\n    Older Claude Code CLI versions returned a single envelope object. Newer\n    versions (>= ~2.1) emit a JSON ARRAY of streamed event objects (a system\n    init event, assistant turns, an optional rate_limit_event, and a final\n    {\"type\":\"result\"} object). Normalize both shapes to the result dict that\n    carries `result`, `usage`, `modelUsage`, and `stop_reason`.\n    \"\"\"\n    try:\n        envelope = json.loads(stdout)\n    except json.JSONDecodeError as exc:\n        raise RuntimeError(\n            f\"claude -p produced unparseable JSON envelope: {exc}; \"\n            f\"first 500 chars of stdout: {stdout[:500]!r}\"\n        ) from exc\n    if isinstance(envelope, list):\n        result_events = [\n            e for e in envelope\n            if isinstance(e, dict) and e.get(\"type\") == \"result\"\n        ]\n        if result_events:\n            return result_events[-1]\n        if envelope and isinstance(envelope[-1], dict):\n            return envelope[-1]\n        raise RuntimeError(\n            \"claude -p returned a JSON array with no result object; \"\n            f\"first 500 chars of stdout: {stdout[:500]!r}\"\n        )\n    return envelope\n","sourceCodeStart":1347,"sourceCodeEnd":1383,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/llm.py#L1347-L1383","documentation":"Raised by `_claude_cli_envelope` when `json.loads(stdout)` fails on the output of `claude -p --output-format json`. graphify expects the CLI's stdout to be a single JSON object (older CLIs) or a JSON array of streamed events (newer CLIs ≥ ~2.1); anything unparseable — interleaved warnings, banners, partial writes — triggers this error, including the first 500 chars of what was actually received to aid diagnosis.","triggerScenarios":"Running the `claude-cli` backend where `claude -p` writes non-JSON to stdout: a CLI version with a changed output format, ANSI/progress output mixed into stdout, a truncated run killed by the subprocess timeout, or plugin/startup messages the CLI prints before the JSON envelope.","commonSituations":"Upgrading (or pinning an old) Claude Code CLI whose output format graphify doesn't recognize; running in a terminal/wrapper that appends text to stdout; `claude` not authenticated so it prints a login prompt instead of JSON; stdout truncated when the process hits the `_resolve_api_timeout()` bound.","solutions":["Update Claude Code CLI to a current version (`claude update` or reinstall via npm) — the envelope formats supported cover recent releases.","Run `claude -p --output-format json \"hi\"` manually and confirm stdout is pure JSON; if not, find what is polluting it (shell profile banners, plugins, progress bars).","Authenticate first: run `claude` once interactively so the headless `-p` mode doesn't emit login prose.","If the run timed out, raise GRAPHIFY_API_TIMEOUT / --api-timeout so the full envelope is flushed."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import json, subprocess\n\ndef claude_cli_emits_json() -> bool:\n    p = subprocess.run([\"claude\", \"-p\", \"--output-format\", \"json\", \"say ok\"],\n                       capture_output=True, text=True, timeout=60)\n    try:\n        json.loads(p.stdout)\n        return True\n    except json.JSONDecodeError:\n        return False\n\nassert claude_cli_emits_json(), \"claude CLI stdout is not pure JSON; update/reinstall the CLI\"","typeGuard":null,"tryCatchPattern":"try:\n    result = extract_files_direct(files, root, backend=\"claude-cli\")\nexcept RuntimeError as e:\n    if \"unparseable JSON envelope\" in str(e):\n        print(f\"CLI stdout corrupted: {e}\"); raise\n    raise","preventionTips":["Pin a known-good Claude Code CLI version in CI.","Run `claude` once interactively to authenticate before headless use.","Keep shell profiles/plugins from printing banners into stdout of subprocess pipelines."],"tags":["claude-cli","json","subprocess","parsing","version-change"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}