{"record":{"id":"875ea452899908bf","repo":"tinyhumansai/openhuman","slug":"claude-code-driver","errorCode":null,"errorMessage":"[claude-code][driver] {}","messagePattern":"\\[claude-code\\]\\[driver\\] (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/inference/provider/claude_code/driver.rs","lineNumber":493,"sourceCode":"            let _ = child.kill().await;\n            anyhow::bail!(\n                \"[claude-code][driver] turn timed out after {:?}\",\n                TURN_TIMEOUT\n            );\n        }\n    };\n\n    let stderr_text = stderr_task.await.unwrap_or_default();\n\n    if !status.success() {\n        anyhow::bail!(\n            \"[claude-code][driver] exit {:?} stderr={}\",\n            status.code(),\n            stderr_text.trim()\n        );\n    }\n    if let Some(err) = mapper.error.clone() {\n        anyhow::bail!(\"[claude-code][driver] {}\", err);\n    }\n\n    Ok(mapper.into_response())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn write_mcp_http_config_emits_http_url_with_bearer_header() {\n        let dir = tempfile::tempdir().expect(\"tempdir\");\n        let addr: std::net::SocketAddr = \"127.0.0.1:54321\".parse().unwrap();\n        let path = write_mcp_http_config(dir.path(), addr, \"tok-abc123\").expect(\"write config\");\n        let raw = std::fs::read_to_string(&path).expect(\"read config\");\n        let v: serde_json::Value = serde_json::from_str(&raw).expect(\"valid json\");\n        let server = &v[\"mcpServers\"][\"openhuman\"];\n        assert_eq!(","sourceCodeStart":475,"sourceCodeEnd":511,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/inference/provider/claude_code/driver.rs#L475-L511","documentation":"The claude-code turn completed with exit status 0, but while parsing the CLI's stream-json events, the response mapper recorded a terminal error (e.g. an `error` event or a result message flagged as error). The mapped error string is appended to the message. Process-level checks passed; this is a protocol/semantic error carried inside a successful run.","triggerScenarios":"The CLI emits a stream-json error event mid-turn (API overload, permission denial for a tool, context-length exceeded) but still exits 0. `mapper.error` is `Some`, so the driver converts it into a failure for the caller.","commonSituations":"Anthropic-side errors (429/529) surfaced as in-stream events; tool-use denials under the `acceptEdits`/limited permission posture; conversation exceeding the model context window mid-turn.","solutions":["Read the `{}` payload — it is the CLI's error verbatim and names the real cause.","Rate-limit/overload: retry with backoff.","Context-length errors: start a fresh session (drop cc_session_id continuation) or trim history.","Permission-denied tool errors: run with full-access posture or allow the specific tool in the project's claude settings."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match driver.run(ctx).await {\n    Err(e) => {\n        let s = e.to_string();\n        if s.starts_with(\"[claude-code][driver]\") && !s.contains(\"exit\") && !s.contains(\"timed out\") {\n            // in-stream CLI error: branch on content (rate limit vs context vs permission)\n            classify_and_handle(&s);\n        }\n        Err(e)\n    }\n    ok => ok,\n}","preventionTips":["Trim conversation history for long sessions to avoid mid-turn context overflow.","Align permission posture with the tools the turn needs to avoid in-stream permission errors.","Treat in-stream 429/529 text as retryable with backoff."],"tags":["claude-code","stream-json","mapper","inference"],"backgroundTag":"cli-agent-turn-failed","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}