{"record":{"id":"73ee59c36da27ec1","repo":"aaif-goose/goose","slug":"tool-call-action-required-message-tool-call-re","errorCode":null,"errorMessage":"Tool call action-required {message}: {tool_call_request_id}","messagePattern":"Tool call action-required (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose/src/action_required_manager.rs","lineNumber":106,"sourceCode":"            response_tx: Some(tx),\n        };\n        let pending_request = Arc::new(Mutex::new(pending_request));\n\n        self.pending\n            .write()\n            .await\n            .insert(id.clone(), Arc::clone(&pending_request));\n\n        let action_required_message = Message::assistant().with_content(\n            MessageContent::action_required_elicitation(id.clone(), message, schema),\n        );\n        if let Err(error) = sender.try_send(action_required_message) {\n            self.pending.write().await.remove(&id);\n            let message = match error {\n                mpsc::error::TrySendError::Full(_) => \"stream is full\",\n                mpsc::error::TrySendError::Closed(_) => \"stream closed\",\n            };\n            return Err(anyhow::anyhow!(\n                \"Tool call action-required {message}: {tool_call_request_id}\"\n            ));\n        }\n\n        let result = self\n            .wait_for_response(&id, pending_request, rx, timeout_duration)\n            .await;\n\n        self.pending.write().await.remove(&id);\n\n        result\n    }\n\n    pub(crate) async fn claim_response(\n        &self,\n        session_id: &str,\n        request_id: &str,\n    ) -> Result<PendingResponseClaim> {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose/src/action_required_manager.rs#L88-L124","documentation":"After registering the pending request, request_elicitation pushes an action-required message onto the session's bounded mpsc stream with try_send. Failure means either 'stream is full' (channel at capacity, consumer not draining) or 'stream closed' (receiver dropped); the message text distinguishes which, and the pending entry is cleaned up before erroring.","triggerScenarios":"The client stopped reading the session message stream (deadlocked or slow consumer) so the bounded channel fills; or the client disconnected and the stream receiver was dropped while the tool call tried to raise a permission prompt.","commonSituations":"Desktop/UI client stalled on a long render loop while many action-required prompts queue up; automation that creates tool calls without consuming the message stream; client crash mid-session.","solutions":["Ensure the session message stream is continuously consumed — keep the reader task alive while tool calls run","If capacity is configurable, raise the channel size for workloads that burst many elicitation prompts","Restart/reconnect the client so a fresh stream exists, then retry the tool call"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match manager.request_elicitation(sid, tcid, msg, schema, d).await {\n    Err(e) if e.to_string().contains(\"stream is full\") => {\n        tokio::time::sleep(Duration::from_millis(200)).await; // let consumer drain\n        manager.request_elicitation(sid, tcid, msg, schema, d).await\n    }\n    Err(e) if e.to_string().contains(\"stream closed\") => {\n        tracing::warn!(\"client gone; aborting elicitation\");\n        Err(e)\n    }\n    r => r,\n}","preventionTips":["Keep a reader task draining the session message stream whenever tool calls can elicit","Do not pause stream consumption during long UI operations"],"tags":["backpressure","channel","elicitation","stream"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}