{"record":{"id":"654d658eccccf144","repo":"headroomlabs-ai/headroom","slug":"join-cmd-failed-exit-result-returncode","errorCode":null,"errorMessage":"`{' '.join(cmd)}` failed (exit {result.returncode}):\n{stderr_snippet}","messagePattern":"`(.+?)` failed \\(exit (.+?)\\):\n(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"headroom/learn/analyzer.py","lineNumber":615,"sourceCode":"            ) from None\n        cmd = shim_cmd\n        try:\n            result = run(cmd, input=prompt, capture_output=True, text=True, timeout=hard_cap)\n        except FileNotFoundError:\n            raise RuntimeError(\n                f\"`{cmd[0]}` not found in PATH. Install it or use a different backend \"\n                \"with --model <litellm-model-name>.\"\n            ) from None\n    except subprocess.TimeoutExpired:\n        raise RuntimeError(\n            f\"`{' '.join(cmd)}` did not respond within {hard_cap}s. \"\n            \"Check network connectivity, raise HEADROOM_LEARN_CLI_TIMEOUT_SECS, \"\n            \"or try a different backend with --model <litellm-model-name>.\"\n        ) from None\n\n    if result.returncode != 0:\n        stderr_snippet = (result.stderr or \"\")[:_MAX_SNIPPET_LEN]\n        raise RuntimeError(\n            f\"`{' '.join(cmd)}` failed (exit {result.returncode}):\\n{stderr_snippet}\"\n        )\n\n    # Log stderr warnings even on success (auth refreshes, deprecation notices).\n    if result.stderr and result.stderr.strip():\n        logger.debug(\"CLI stderr (exit 0): %s\", result.stderr[:_MAX_SNIPPET_LEN])\n\n    try:\n        return _strip_fenced_json(result.stdout)\n    except json.JSONDecodeError as exc:\n        stdout_snippet = (result.stdout or \"\")[:_MAX_SNIPPET_LEN]\n        raise RuntimeError(\n            f\"`{' '.join(cmd)}` returned unparseable output. \"\n            f\"First {_MAX_SNIPPET_LEN} chars:\\n{stdout_snippet}\"\n        ) from exc\n\n\ndef _call_claude_cli_streaming(","sourceCodeStart":597,"sourceCodeEnd":633,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/learn/analyzer.py#L597-L633","documentation":"Raised after the CLI subprocess completed but exited non-zero: result.returncode != 0. The RuntimeError embeds the exit code plus the first _MAX_SNIPPET_LEN chars of stderr, so the actual CLI failure (auth error, quota, bad flags, model access denied) appears in the message. headroom treats any non-zero exit as a failed analysis.","triggerScenarios":"`headroom learn` invokes the CLI backend and it exits non-zero: expired/invalid CLI credentials, plan limits, the CLI rejecting the piped prompt flags (e.g. unsupported --model or output-format flags after a CLI update), or sandbox/permission denials inside the CLI itself.","commonSituations":"Claude/Gemini/Codex CLI login token expired since last use; CLI auto-updated and changed flag semantics so headroom's hardcoded cmd_parts no longer work; org policy/VPN blocking the CLI's API; quota exhausted mid-run.","solutions":["Read the embedded stderr snippet — it names the actual CLI failure; fix that first (re-auth with `claude login` / `gemini auth` etc.)","Update headroom (its cmd_parts may be fixed for newer CLIs) and/or update the CLI to a compatible version","Test the CLI standalone with a trivial prompt to isolate auth vs. flag problems","Fall back to an API model: export a key and run headroom learn --model <litellm-model-name>"],"exampleFix":"# before\nheadroom learn  # `claude ...` failed (exit 1): authentication expired\n\n# after\nclaude login  # or: gemini auth apply\nheadroom learn","handlingStrategy":"try-catch","validationCode":"import subprocess\nr = subprocess.run([cli, '--version'], capture_output=True, text=True)\nif r.returncode != 0:\n    raise SystemExit(f'{cli} unhealthy (exit {r.returncode}): {r.stderr[:200]} — re-auth before headroom learn')","typeGuard":null,"tryCatchPattern":"try:\n    recs = run_learn()\nexcept RuntimeError as e:\n    if 'failed (exit' in str(e):\n        logger.error('CLI backend failed: %s', e)\n        os.environ.pop('HEADROOM_LEARN_CLI', None)  # try API backend next\n        recs = run_learn()\n    else:\n        raise","preventionTips":["Keep CLI auth fresh — run a trivial prompt through the CLI periodically","Update headroom and the CLIs together; flag drift breaks hardcoded cmd_parts","Parse the stderr snippet from the message; it usually names the root cause"],"tags":["cli","exit-code","subprocess","auth"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}