aaif-goose/goose · warning

Request already completed: {}

Error message

Request already completed: {}

What it means

PendingResponseClaim::submit moves the response_tx out of the pending request via take(); a second submit on the same claim (or on a request whose tx was already consumed) finds None and fails with this message. It enforces exactly-one-response semantics for action-required elicitations — the guard against double answering.

Source

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

}

struct PendingRequest {
    session_id: String,
    response_tx: Option<tokio::sync::oneshot::Sender<ElicitationOutcome>>,
}

pub(crate) struct PendingResponseClaim {
    request_id: String,
    pending: OwnedMutexGuard<PendingRequest>,
}

impl PendingResponseClaim {
    pub(crate) fn submit(mut self, response: ElicitationOutcome) -> Result<()> {
        let tx = self
            .pending
            .response_tx
            .take()
            .ok_or_else(|| anyhow::anyhow!("Request already completed: {}", self.request_id))?;
        drop(self.pending);

        if tx.send(response).is_err() {
            return Err(anyhow::anyhow!("Response channel closed"));
        }

        Ok(())
    }
}

pub(crate) struct ActionRequiredManager {
    pending: Arc<RwLock<HashMap<String, Arc<Mutex<PendingRequest>>>>>,
    action_required_senders: Mutex<HashMap<(String, String), mpsc::Sender<Message>>>,
}

impl ActionRequiredManager {
    pub(crate) fn new() -> Self {
        Self {

View on GitHub (pinned to 3810898a74)

Solutions

  1. Ensure only one responder owns the claim — consume PendingResponseClaim exactly once per request id
  2. Deduplicate at the call site: guard with an atomic 'answered' flag or drop the claim after first use
  3. Treat a second submit as a no-op (catch and ignore 'Request already completed') rather than an error path

Example fix

// before
claim.submit(outcome).await?;
claim.submit(outcome).await?; // duplicate

// after
if let Err(e) = claim.submit(outcome).await {
    tracing::debug!(%e, "elicitation already answered");
}
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = claim.submit(outcome).await {
    if e.to_string().contains("already completed") {
        tracing::debug!("duplicate elicitation response ignored");
        return Ok(()); // first response stands
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Two code paths respond to the same elicitation request id: a UI button handler and a programmatic responder both calling submit; or a retry of a submit that already succeeded.

Common situations: Client code that answers an elicitation and then answers again on timeout/cancel; duplicated event handlers firing for one action; race between an explicit user answer and an automatic timeout responder.

Related errors


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