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
- Hold the ThreadEvent receiver for the entire lifetime of the thread
- Treat this error as cancellation: abort the turn and clean up tool state
- On reconnect, restart the thread or resend the prompt instead of replaying the pending authorization
- 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
- Hold the ThreadEvent receiver until the thread is finished; dropping it early turns every pending authorization into an error
- Answer every ToolCallAuthorization request, including when the UI is closing (respond with a deny instead of dropping)
- Cancel the thread before tearing down the UI so prompts resolve as cancelled turns
- In embedding code, scope the event receiver to the thread's lifetime, not to a shorter task
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
- Failed to send Windows-drive sandbox warning: {error}
- Failed to send sandbox fallback authorization: {error}
- authorization channel closed
- Failed to send tool call decision prompt: {error}
- Permission to run tool denied by user
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/92444df8727a7683.
Report an issue: GitHub.