aaif-goose/goose · warning

Request not found: {}

Error message

Request not found: {}

What it means

Thrown by ActionRequiredManager::pending_request when the request_id is not present in the in-memory pending map. The map is populated by request_and_wait and the entry is removed as soon as the request completes, times out, or fails to enqueue — so an unknown id means the elicitation is no longer answerable (or never existed in this process).

Source

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

            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Request already completed: {}", request_id))?;
        if tx.is_closed() {
            pending.response_tx.take();
            return Err(anyhow::anyhow!("Response channel closed"));
        }

        Ok(PendingResponseClaim {
            request_id: request_id.to_string(),
            pending,
        })
    }

    async fn pending_request(&self, request_id: &str) -> Result<Arc<Mutex<PendingRequest>>> {
        let pending = self.pending.read().await;
        pending
            .get(request_id)
            .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"));
                }

View on GitHub (pinned to 3810898a74)

Solutions

  1. Answer the elicitation once, within its lifetime — before it completes or times out
  2. Use the exact request id from the most recent action_required message in the live conversation, not one restored from an old session
  3. If the id is unknown, continue with a normal user message instead of an elicitation response
  4. Guard double-submits in the UI (disable the button after first answer)

Example fix

// before
let claim = manager.claim_response(&session_id, &request_id).await?;

// after
let claim = match manager.claim_response(&session_id, &request_id).await {
    Ok(claim) => claim,
    Err(e) if e.to_string().contains("Request not found") => {
        // already answered, expired, or from a previous run — nothing to do
        return Ok(());
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Only answer ids that appear as unanswered action_required messages in the
// live conversation:
let is_open = conversation.messages.iter().any(|m| {
    m.content.iter().any(|c| matches!(c,
        MessageContent::ActionRequiredElicitation { id, .. } if id == request_id))
});

Type guard

fn request_not_found(e: &anyhow::Error) -> bool {
    e.to_string().contains("Request not found")
}

Try / catch

match manager.claim_response(&session_id, &request_id).await {
    Ok(claim) => { /* submit */ }
    Err(e) if request_not_found(&e) => {
        // already answered, expired, or from a previous process run — no-op
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Responding to an elicitation that already completed (entry removed after wait_for_response returns); responding after the waiter timed out and cleaned up (line self.pending.write().await.remove(&id)); using a request_id from a previous process run — the map is per-process and not restored from session history; typo'd or fabricated request_id.

Common situations: Double-submitting an elicitation answer (double-click on Accept/Decline in the UI); answering a stale action_required message reloaded from a resumed session after goose restarted; two UI clients answering the same request.

Related errors


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