{"record":{"id":"e60bfbfed03acd6c","repo":"zed-industries/zed","slug":"failed-to-send-tool-call-authorization-error","errorCode":null,"errorMessage":"Failed to send tool call authorization: {error}","messagePattern":"Failed to send tool call authorization: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/agent/src/thread.rs","lineNumber":6442,"sourceCode":"        cx.spawn(async move |cx| {\n            let (response_tx, mut response_rx) = oneshot::channel();\n            if let Err(error) = stream\n                .0\n                .unbounded_send(Ok(ThreadEvent::ToolCallAuthorization(\n                    ToolCallAuthorization {\n                        tool_call: acp::ToolCallUpdate::new(\n                            tool_call_id.clone(),\n                            acp::ToolCallUpdateFields::new().title(title),\n                        ),\n                        options,\n                        response: response_tx,\n                        context,\n                        kind: acp_thread::AuthorizationKind::PermissionGrant,\n                    },\n                )))\n            {\n                log::error!(\"Failed to send tool call authorization: {error}\");\n                return Err(anyhow!(\"Failed to send tool call authorization: {error}\"));\n            }\n\n            let Some(check_settings) = check_settings else {\n                let outcome = response_rx\n                    .await\n                    .map_err(|_| anyhow!(\"authorization channel closed\"))?;\n                ensure_tool_call_authorization_not_interrupted(&outcome)?;\n\n                return Self::persist_permission_outcome(&outcome, fs, cx);\n            };\n            let Some((auto_allow_outcome, auto_deny_outcome)) = auto_resolution_outcomes else {\n                return Err(anyhow!(\"missing auto-resolution outcomes\"));\n            };\n\n            let (mut settings_tx, mut settings_rx) = watch::channel(());\n            let _settings_subscription = cx.update(|cx| {\n                cx.observe_global::<SettingsStore>(move |_cx| {\n                    settings_tx.send(()).ok();","sourceCodeStart":6424,"sourceCodeEnd":6460,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/agent/src/thread.rs#L6424-L6460","documentation":"Raised by Zed's agent thread when a tool-call authorization (permission) request cannot be delivered to the UI/client that owns the prompt. The request is sent over a channel inside a `RequestToolCallAuthorization` event; a send failure means the receiving half was already dropped. In practice the thread, agent panel, or ACP session went away between the tool call starting and the prompt being raised.","triggerScenarios":"A permission-requiring tool runs while the event receiver is gone: the agent panel or window was closed, the run/thread was cancelled, the ACP client disconnected, or a test spawned the thread without anything polling its event stream. The `send(...)` on the authorization event channel returns Err and this branch fires.","commonSituations":"Pressing stop or closing the agent panel at the instant a prompt would appear; running the agent headlessly with no authorization UI attached; integration tests that tear down the app context before answering prompts.","solutions":["Treat the failed send as cancellation: abort the tool call and report 'permission prompt unavailable' instead of retrying the send.","Verify a live consumer of `RequestToolCallAuthorization` events (agent panel / ACP bridge) exists before spawning permission-requiring tools.","In tests, pump the event loop and answer `response_tx` so the channel always has a live receiver.","Audit shutdown ordering so the thread is not dropped while tool tasks are still waiting on a prompt."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before starting permission-requiring tools, confirm the thread's event loop is alive:\nif !authorization_events_being_served(&thread, cx) {\n    anyhow::bail!(\"agent thread is shutting down; skip the tool call\");\n}","typeGuard":null,"tryCatchPattern":"match send_authorization_request(&thread, request).await {\n    Ok(outcome) => handle(outcome),\n    Err(err) if err.to_string().starts_with(\"Failed to send tool call authorization\") => {\n        // Prompt consumer is gone: treat as cancellation, not a crash.\n        report_cancelled();\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Never drop the agent thread while tool calls await permission — cancel them first.","Keep exactly one live consumer for RequestToolCallAuthorization events per thread.","In tests, pump the event loop and answer authorization requests so sends never fail."],"tags":["agent","permissions","channel","async","shutdown"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}