{"record":{"id":"df28458f7eb401e9","repo":"Graphify-Labs/graphify","slug":"claude-p-reported-an-error-cli-error-500","errorCode":null,"errorMessage":"claude -p reported an error: {cli_error[:500]}","messagePattern":"claude -p reported an error: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"graphify/llm.py","lineNumber":1579,"sourceCode":"    if _claude_cli_supports_json_schema(claude_cmd):\n        cli_args.extend([\"--json-schema\", _EXTRACTION_JSON_SCHEMA])\n    proc = subprocess.run(\n        cli_args,\n        input=combined_message,\n        capture_output=True,\n        text=True,\n        encoding=\"utf-8\",  # Force UTF-8 — prevents UnicodeEncodeError on Windows cp1252\n        errors=\"replace\",  # Tolerate non-UTF-8 bytes (e.g. GBK/cp936 from claude.cmd on Chinese Windows)\n        timeout=_resolve_api_timeout(),\n        check=False,\n        **_no_window_kwargs(),\n    )\n    cli_error = _claude_cli_error(proc.stdout)\n    if proc.returncode != 0:\n        detail = proc.stderr.strip() or cli_error or \"(no stderr, no error envelope)\"\n        raise RuntimeError(f\"claude -p exited {proc.returncode}: {detail[:500]}\")\n    if cli_error:\n        raise RuntimeError(f\"claude -p reported an error: {cli_error[:500]}\")\n\n    envelope = _claude_cli_envelope(proc.stdout)\n\n    # When --json-schema is in effect the CLI puts the CONSTRAINED object in the\n    # `structured_output` envelope field; `result` stays the model's discretionary\n    # text, which on a \"reporting\" turn is prose even with the flag set (verified\n    # live on Claude Code 2.1.185). Prefer the structured channel and route it\n    # through the same _parse_llm_json normalizer; fall back to parsing `result`\n    # for older CLIs that don't emit structured_output (#2076 review).\n    structured = envelope.get(\"structured_output\")\n    if isinstance(structured, dict):\n        raw_content = json.dumps(structured)\n    else:\n        raw_content = envelope.get(\"result\", \"\")\n    result = _parse_llm_json(raw_content or \"{}\")\n    usage = envelope.get(\"usage\") or {}\n    result[\"input_tokens\"] = (\n        int(usage.get(\"input_tokens\", 0) or 0)","sourceCodeStart":1561,"sourceCodeEnd":1597,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/llm.py#L1561-L1597","documentation":"Raised when `claude -p` exited 0 but the stdout JSON envelope carries an error (the `_claude_cli_error` extraction found `is_error` in the envelope). Per the adjacent comment, the CLI reports API failures such as rate limits and auth errors in the stdout JSON with `is_error: true` while leaving stderr empty — and on rate limits it still exits 0 — so exit-code checks alone miss it. The CLI's own error text is included (truncated to 500 chars).","triggerScenarios":"Hitting Anthropic API rate limits or an auth/API error during a `claude-cli` extraction: exit code 0, stderr empty, envelope `is_error: true`. Typical during large batch extractions that fire many `claude -p` calls in quick succession.","commonSituations":"Bulk graphify runs over many chunks exceeding the account's request/token rate limit; subscription usage caps; expired credentials after 0-exit success of previous runs; org-level throttling.","solutions":["If the message says rate limit: back off and retry — add spacing between extraction calls or retry after the window resets (graphify's chunk-level retry/skip can also absorb it).","If auth-related: re-run `claude` interactively to refresh credentials.","Reduce parallelism / batch size of the graphify run to stay under limits.","Inspect the full envelope by reproducing with `claude -p --output-format json` to confirm which limit tripped."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"import time\nfor attempt in range(4):\n    try:\n        result = extract_files_direct(chunk, root, backend=\"claude-cli\")\n        break\n    except RuntimeError as e:\n        if \"reported an error\" not in str(e) or attempt == 3:\n            raise\n        if \"rate\" in str(e).lower():\n            time.sleep(60 * (attempt + 1))  # rate limit: back off\n        else:\n            raise","preventionTips":["Throttle bulk claude-cli extraction: space requests and cap parallelism below account rate limits.","Treat exit code 0 as unreliable for the claude-cli backend — always parse the envelope's error field (graphify does; your retry logic should key on the message text).","Log the CLI's error text so limit vs auth failures are distinguishable in postmortems."],"tags":["claude-cli","rate-limit","api-error","retry"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}