{"record":{"id":"8a0ca5f770d14405","repo":"tinyhumansai/openhuman","slug":"claude-code-semaphore-closed-e","errorCode":null,"errorMessage":"claude-code semaphore closed: {e}","messagePattern":"claude-code semaphore closed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/inference/provider/claude_code/mod.rs","lineNumber":171,"sourceCode":"                \"[claude-code] `claude` CLI at {} unusable: {}\",\n                path,\n                reason\n            ),\n        }\n    }\n\n    async fn run_chat(\n        &self,\n        request: ChatRequest<'_>,\n        model_override: Option<&str>,\n    ) -> anyhow::Result<ChatResponse> {\n        // Cap concurrent CC processes.\n        let _permit = self\n            .semaphore\n            .clone()\n            .acquire_owned()\n            .await\n            .map_err(|e| anyhow::anyhow!(\"claude-code semaphore closed: {e}\"))?;\n\n        // Extract system prompt + thread_id from the request.\n        let append_system_prompt = request\n            .messages\n            .iter()\n            .find(|m| m.role == \"system\")\n            .map(|m| m.content.clone());\n\n        // OpenHuman doesn't pass thread_id directly through ChatRequest yet\n        // (Phase 4 will). For Phase 2 we key sessions on a stable hash of\n        // the conversation so /resume kicks in across consecutive turns.\n        let thread_id = thread_key_from_messages(request.messages);\n\n        let model = model_override.unwrap_or(&self.model).to_string();\n\n        let turn = driver::TurnContext {\n            bin_path: self.bin_path.clone(),\n            workspace_dir: self.workspace_dir.clone(),","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/inference/provider/claude_code/mod.rs#L153-L189","documentation":"Acquiring the concurrency permit that caps simultaneous Claude Code processes failed because the semaphore was closed. The permit is released per turn; a closed semaphore means the provider instance is being torn down while a chat request is still arriving — an invariant/lifecycle error, not load-related.","triggerScenarios":"Thrown at src/openhuman/inference/provider/claude_code/mod.rs:171 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Ensure the provider is not used after shutdown","Check for requests racing provider teardown"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}