aaif-goose/goose · warning

Timeout waiting for user response

Error message

Timeout waiting for user response

What it means

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.

Source

Thrown at crates/goose/src/action_required_manager.rs:174

            .cloned()
            .ok_or_else(|| anyhow::anyhow!("Request not found: {}", request_id))
    }

    async fn wait_for_response(
        &self,
        request_id: &str,
        pending_request: Arc<Mutex<PendingRequest>>,
        mut rx: tokio::sync::oneshot::Receiver<ElicitationOutcome>,
        timeout_duration: Duration,
    ) -> Result<ElicitationOutcome> {
        match timeout(timeout_duration, &mut rx).await {
            Ok(response) => Self::finish_waiting(request_id, response),
            Err(_) => {
                let mut pending = pending_request.lock().await;
                if pending.response_tx.is_some() {
                    pending.response_tx.take();
                    warn!("Timeout waiting for response: {}", request_id);
                    return Err(anyhow::anyhow!("Timeout waiting for user response"));
                }
                drop(pending);

                Self::finish_waiting(request_id, rx.await)
            }
        }
    }

    fn finish_waiting(
        request_id: &str,
        response: Result<ElicitationOutcome, tokio::sync::oneshot::error::RecvError>,
    ) -> Result<ElicitationOutcome> {
        match response {
            Ok(user_data) => Ok(user_data),
            Err(_) => {
                warn!("Response channel closed for request: {}", request_id);
                Err(anyhow::anyhow!("Response channel closed"))
            }

View on GitHub (pinned to 3810898a74)

Solutions

  1. Answer the elicitation before the timeout expires
  2. Pass a longer timeout_duration to request_and_wait when the caller controls it
  3. Before issuing the elicitation, verify a consumer exists via has_action_required_stream so the prompt is actually reachable
  4. Handle the error as a Decline/Cancel and re-elicit or continue instead of aborting the run

Example fix

// before
let outcome = manager
    .request_and_wait(session_id, req_id, message, schema, Duration::from_secs(30))
    .await?;

// after
let outcome = match manager
    .request_and_wait(session_id, req_id, message, schema, Duration::from_secs(600))
    .await
{
    Ok(outcome) => outcome,
    Err(e) if e.to_string().contains("Timeout waiting for user response") => {
        ElicitationOutcome::Cancel
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: fallback

Validate before calling

// Ensure the prompt can actually reach a consumer before asking:
if !manager
    .has_action_required_stream(&session_id, &tool_call_request_id)
    .await
{
    return Ok(ElicitationOutcome::Decline); // nobody can answer; don't block
}

Type guard

fn elicitation_timed_out(e: &anyhow::Error) -> bool {
    e.to_string().contains("Timeout waiting for user response")
}

Try / catch

let outcome = match manager.request_and_wait(/* ... */).await {
    Ok(outcome) => outcome,
    Err(e) if elicitation_timed_out(&e) => {
        // fall back to a safe default instead of failing the tool call
        ElicitationOutcome::Decline
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/ce6c94f6fa15e1ba. Report an issue: GitHub.