zed-industries/zed · error

Failed to send sandbox authorization: {error}

Error message

Failed to send sandbox authorization: {error}

What it means

Before running a sandboxed tool call, the thread offers an authorization request over its event stream; if unbounded_send of ThreadEvent::ToolCallAuthorization fails, the consumer of the thread's events (the client UI) is gone and this error wraps the send failure. A permission request cannot be delivered to a dropped receiver, so the tool call fails.

Source

Thrown at crates/agent/src/thread.rs:5874

                    ToolCallAuthorization {
                        tool_call: acp::ToolCallUpdate::new(
                            tool_call_id.clone(),
                            // Leave the title untouched so the card keeps
                            // showing the command (matching the fallback flow).
                            acp::ToolCallUpdateFields::new(),
                        )
                        .meta(acp_thread::meta_with_sandbox_authorization(
                            sandbox_authorization_details,
                        )),
                        options,
                        response: response_tx,
                        context: None,
                        kind: acp_thread::AuthorizationKind::PermissionGrant,
                    },
                )))
            {
                log::error!("Failed to send sandbox authorization: {error}");
                return Err(anyhow!("Failed to send sandbox authorization: {error}"));
            }

            let (mut settings_tx, mut settings_rx) = watch::channel(());
            let _settings_subscription = cx.update(|cx| {
                cx.observe_global::<SettingsStore>(move |_cx| {
                    settings_tx.send(()).ok();
                })
            });

            loop {
                let settings_changed = async {
                    if settings_rx.changed().await.is_err() {
                        std::future::pending::<()>().await;
                    }
                };
                futures::select_biased! {
                    outcome = (&mut response_rx).fuse() => {
                        let outcome = outcome.map_err(|_| anyhow!("authorization channel closed"))?;

View on GitHub (pinned to bc538def45)

Solutions

  1. Hold the ThreadEvent receiver for the entire lifetime of the thread
  2. Treat this error as cancellation: abort the turn and clean up tool state
  3. On reconnect, restart the thread or resend the prompt instead of replaying the pending authorization
  4. Cancel the thread before tearing down the UI so prompts resolve as cancelled turns

Example fix

// before: receiver dropped as soon as the spawn ends
let _events = thread.update(cx, |t, cx| t.send_existing(cx))?;

// after: keep the receiver alive while the thread runs
let mut events = thread.update(cx, |t, cx| t.send_existing(cx))?;
while let Some(event) = events.next().await {
    handle(event)?; // also answers authorization requests
}
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(err) = authorization_result {
    if err.to_string().starts_with("Failed to send sandbox authorization") {
        // Event consumer is gone: treat as session cancellation, stop the turn.
        stop_turn_and_cleanup();
        return Ok(());
    }
    return Err(err);
}

Prevention

When it happens

Trigger: The thread event receiver is dropped while a sandboxed tool call awaits permission: the agent panel or window was closed, the session was disconnected, or embedding code dropped the event stream early.

Common situations: Closing the UI mid permission prompt; a client crash or reload during a sandboxed command; embedding code that scopes the event receiver shorter than the thread lifetime.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/92444df8727a7683. Report an issue: GitHub.