{"record":{"id":"4892aa36e28cb26c","repo":"aaif-goose/goose","slug":"response-channel-closed","errorCode":null,"errorMessage":"Response channel closed","messagePattern":"Response channel closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/goose/src/action_required_manager.rs","lineNumber":42,"sourceCode":"    response_tx: Option<tokio::sync::oneshot::Sender<ElicitationOutcome>>,\n}\n\npub(crate) struct PendingResponseClaim {\n    request_id: String,\n    pending: OwnedMutexGuard<PendingRequest>,\n}\n\nimpl PendingResponseClaim {\n    pub(crate) fn submit(mut self, response: ElicitationOutcome) -> Result<()> {\n        let tx = self\n            .pending\n            .response_tx\n            .take()\n            .ok_or_else(|| anyhow::anyhow!(\"Request already completed: {}\", self.request_id))?;\n        drop(self.pending);\n\n        if tx.send(response).is_err() {\n            return Err(anyhow::anyhow!(\"Response channel closed\"));\n        }\n\n        Ok(())\n    }\n}\n\npub(crate) struct ActionRequiredManager {\n    pending: Arc<RwLock<HashMap<String, Arc<Mutex<PendingRequest>>>>>,\n    action_required_senders: Mutex<HashMap<(String, String), mpsc::Sender<Message>>>,\n}\n\nimpl ActionRequiredManager {\n    pub(crate) fn new() -> Self {\n        Self {\n            pending: Arc::new(RwLock::new(HashMap::new())),\n            action_required_senders: Mutex::new(HashMap::new()),\n        }\n    }","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose/src/action_required_manager.rs#L24-L60","documentation":"submit() got the oneshot sender but tx.send() failed, meaning the receiving half (wait_for_response in request_elicitation) was dropped first. The waiter left: the elicitation timed out (timeout_duration elapsed), the session was cancelled, or the requesting task aborted — so the answer has nowhere to go.","triggerScenarios":"The user answers an action-required prompt after the configured timeout_duration expired and wait_for_response returned Err(timeout) and dropped rx; or the session/tool-call task is cancelled while an elicitation dialog is still open.","commonSituations":"Slow human responses to permission prompts with short timeouts; closing a session mid-elicitation; client disconnects while a dialog is pending.","solutions":["Increase the elicitation timeout_duration so the waiter outlives realistic response times","If the cancellation was intended (session closed), treat this error as benign and log it at debug level","Cancel/close outstanding elicitation dialogs when a session ends, so answers are never submitted after the fact"],"exampleFix":"// before\nlet timeout = Duration::from_secs(30);\n\n// after\nlet timeout = Duration::from_secs(300);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = claim.submit(outcome).await {\n    if e.to_string() == \"Response channel closed\" {\n        tracing::debug!(\"requester gone (timeout/cancel); response discarded\");\n        return Ok(());\n    }\n    return Err(e);\n}","preventionTips":["Size elicitation timeouts to realistic human response times","Close open elicitation dialogs when their session ends"],"tags":["elicitation","timeout","channel","cancellation"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}