{"record":{"id":"ce6c94f6fa15e1ba","repo":"aaif-goose/goose","slug":"timeout-waiting-for-user-response","errorCode":null,"errorMessage":"Timeout waiting for user response","messagePattern":"Timeout waiting for user response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/goose/src/action_required_manager.rs","lineNumber":174,"sourceCode":"            .cloned()\n            .ok_or_else(|| anyhow::anyhow!(\"Request not found: {}\", request_id))\n    }\n\n    async fn wait_for_response(\n        &self,\n        request_id: &str,\n        pending_request: Arc<Mutex<PendingRequest>>,\n        mut rx: tokio::sync::oneshot::Receiver<ElicitationOutcome>,\n        timeout_duration: Duration,\n    ) -> Result<ElicitationOutcome> {\n        match timeout(timeout_duration, &mut rx).await {\n            Ok(response) => Self::finish_waiting(request_id, response),\n            Err(_) => {\n                let mut pending = pending_request.lock().await;\n                if pending.response_tx.is_some() {\n                    pending.response_tx.take();\n                    warn!(\"Timeout waiting for response: {}\", request_id);\n                    return Err(anyhow::anyhow!(\"Timeout waiting for user response\"));\n                }\n                drop(pending);\n\n                Self::finish_waiting(request_id, rx.await)\n            }\n        }\n    }\n\n    fn finish_waiting(\n        request_id: &str,\n        response: Result<ElicitationOutcome, tokio::sync::oneshot::error::RecvError>,\n    ) -> Result<ElicitationOutcome> {\n        match response {\n            Ok(user_data) => Ok(user_data),\n            Err(_) => {\n                warn!(\"Response channel closed for request: {}\", request_id);\n                Err(anyhow::anyhow!(\"Response channel closed\"))\n            }","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose/src/action_required_manager.rs#L156-L192","documentation":"Thrown by ActionRequiredManager::wait_for_response when tokio::timeout elapses before the user answers and the oneshot sender is still owned by the pending entry. The waiter takes the sender (so any later answer attempt gets 'Request already completed'/'Response channel closed') and the pending entry is removed by request_and_wait; the elicitation tool call then fails with this error.","triggerScenarios":"request_and_wait is called with a timeout_duration and no ElicitationOutcome arrives in time: the action_required message was never shown (no registered stream, stream closed/full), the user simply didn't respond, or the response was submitted just after the deadline.","commonSituations":"Unattended CLI/desktop session where a permission prompt sits unanswered; UI delivered the prompt but the user is away; a caller that registers no action_required consumer for the (session_id, tool_call_request_id) pair; timeout configured shorter than realistic human response time.","solutions":["Answer the elicitation before the timeout expires","Pass a longer timeout_duration to request_and_wait when the caller controls it","Before issuing the elicitation, verify a consumer exists via has_action_required_stream so the prompt is actually reachable","Handle the error as a Decline/Cancel and re-elicit or continue instead of aborting the run"],"exampleFix":"// before\nlet outcome = manager\n    .request_and_wait(session_id, req_id, message, schema, Duration::from_secs(30))\n    .await?;\n\n// after\nlet outcome = match manager\n    .request_and_wait(session_id, req_id, message, schema, Duration::from_secs(600))\n    .await\n{\n    Ok(outcome) => outcome,\n    Err(e) if e.to_string().contains(\"Timeout waiting for user response\") => {\n        ElicitationOutcome::Cancel\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"fallback","validationCode":"// Ensure the prompt can actually reach a consumer before asking:\nif !manager\n    .has_action_required_stream(&session_id, &tool_call_request_id)\n    .await\n{\n    return Ok(ElicitationOutcome::Decline); // nobody can answer; don't block\n}","typeGuard":"fn elicitation_timed_out(e: &anyhow::Error) -> bool {\n    e.to_string().contains(\"Timeout waiting for user response\")\n}","tryCatchPattern":"let outcome = match manager.request_and_wait(/* ... */).await {\n    Ok(outcome) => outcome,\n    Err(e) if elicitation_timed_out(&e) => {\n        // fall back to a safe default instead of failing the tool call\n        ElicitationOutcome::Decline\n    }\n    Err(e) => return Err(e),\n};","preventionTips":["Size timeout_duration for real human response time (minutes, not seconds)","Register the action_required stream before starting the tool call that elicits","Surf the elicitation prompt immediately in the UI so it isn't discovered late","Re-elicitation after a timeout is cheaper than failing the whole run"],"tags":["rust","goose","elicitation","timeout","user-interaction"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}