{"record":{"id":"5e9cccc654cd1d91","repo":"headroomlabs-ai/headroom","slug":"join-cmd-did-not-emit-a-final-result-ev","errorCode":null,"errorMessage":"`{' '.join(cmd)}` did not emit a final `result` event. First {_MAX_SNIPPET_LEN} chars of stdout:\n{stdout_snippet}","messagePattern":"`(.+?)` did not emit a final `result` event\\. First (.+?) chars of stdout:\n(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"headroom/learn/analyzer.py","lineNumber":769,"sourceCode":"                result_text = event.get(\"result\")\n                if isinstance(result_text, str):\n                    final_result = result_text\n        else:\n            stderr_lines.append(line)\n\n    proc.wait()\n\n    if proc.returncode != 0:\n        stderr_blob = \"\".join(stderr_lines)[:_MAX_SNIPPET_LEN]\n        raise RuntimeError(f\"`{' '.join(cmd)}` failed (exit {proc.returncode}):\\n{stderr_blob}\")\n\n    stderr_blob = \"\".join(stderr_lines)\n    if stderr_blob.strip():\n        logger.debug(\"CLI stderr (exit 0): %s\", stderr_blob[:_MAX_SNIPPET_LEN])\n\n    if final_result is None:\n        stdout_snippet = \"\".join(stdout_lines)[:_MAX_SNIPPET_LEN]\n        raise RuntimeError(\n            f\"`{' '.join(cmd)}` did not emit a final `result` event. \"\n            f\"First {_MAX_SNIPPET_LEN} chars of stdout:\\n{stdout_snippet}\"\n        )\n\n    try:\n        return _strip_fenced_json(final_result)\n    except json.JSONDecodeError as exc:\n        snippet = final_result[:_MAX_SNIPPET_LEN]\n        raise RuntimeError(\n            f\"`{' '.join(cmd)}` returned unparseable output. \"\n            f\"First {_MAX_SNIPPET_LEN} chars:\\n{snippet}\"\n        ) from exc\n\n\ndef _parse_stream_event(line: str) -> dict | None:\n    \"\"\"Parse one line of claude-cli stream-json output, returning None on junk.\"\"\"\n    line = line.strip()\n    if not line:","sourceCodeStart":751,"sourceCodeEnd":787,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/learn/analyzer.py#L751-L787","documentation":"Raised by the streaming analyzer when claude-cli exited cleanly (exit 0) but no JSON event with type==\"result\" was ever seen on stdout. The result event is the only place the assistant's answer lives, so without it there is nothing to parse; the message includes the first _MAX_SNIPPET_LEN chars of captured stdout for diagnosis.","triggerScenarios":"Calling the claude-cli with --output-format stream-json where the model returns an empty response, the CLI emits only system/assistant events and drops the final result event, the stream is truncated by a kill, or an unexpected claude-cli version changes the event schema so _parse_stream_event never matches type 'result'.","commonSituations":"claude-cli version drift changing stream-json event shapes; empty model responses (empty conversation or prompt filtered); upstream CLI bug losing the result event on long outputs; running against a litellm-proxied model that ends the stream without a result frame.","solutions":["Inspect the stdout snippet in the message — if it shows assistant text but no result frame, update claude-cli (`claude update`) to pick up stream fixes.","Retry the run; transient empty responses from the backend are a known shape.","Shrink the digest being analyzed (learn fewer conversations at once) so the response is shorter and the result event is not truncated.","Switch to a different backend with --model <litellm-model-name> to rule out claude-cli-specific behavior.","Report with debug logs if it persists on the latest CLI version."],"exampleFix":"# before\nheadroom learn  # exit 0 but 'did not emit a final `result` event'\n\n# after\nclaude update  # or:\nheadroom learn --model gpt-4o  # different backend","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"last_err = None\nfor attempt in range(2):\n    try:\n        result = run_learn(...)\n        break\n    except RuntimeError as e:\n        if \"did not emit a final `result` event\" in str(e):\n            last_err = e\n            continue\n        raise\nelse:\n    raise SystemExit(f\"backend dropped result event twice; switch --model: {last_err}\")","preventionTips":["Pin a known-good claude-cli version in CI instead of floating latest.","Keep learn digests small so responses finish well within limits.","When this fires once, retry before investigating — transient empty completions are the common case."],"tags":["cli","streaming","protocol","learn","json"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}