aaif-goose/goose · warning

Response channel closed

Error message

Response channel closed

What it means

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.

Source

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

    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 {
            pending: Arc::new(RwLock::new(HashMap::new())),
            action_required_senders: Mutex::new(HashMap::new()),
        }
    }

View on GitHub (pinned to 3810898a74)

Solutions

  1. Increase the elicitation timeout_duration so the waiter outlives realistic response times
  2. If the cancellation was intended (session closed), treat this error as benign and log it at debug level
  3. Cancel/close outstanding elicitation dialogs when a session ends, so answers are never submitted after the fact

Example fix

// before
let timeout = Duration::from_secs(30);

// after
let timeout = Duration::from_secs(300);
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = claim.submit(outcome).await {
    if e.to_string() == "Response channel closed" {
        tracing::debug!("requester gone (timeout/cancel); response discarded");
        return Ok(());
    }
    return Err(e);
}

Prevention

When it happens

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

Common situations: Slow human responses to permission prompts with short timeouts; closing a session mid-elicitation; client disconnects while a dialog is pending.

Related errors


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