zed-industries/zed · error · anyhow::Error
missing auto-resolution outcomes
Error message
missing auto-resolution outcomes
What it means
Internal invariant error in the agent thread's permission flow: settings-based auto-resolution is active (`check_settings` is Some) but `auto_resolution_outcomes` — the canned Allow/Deny outcomes used to auto-resolve the prompt — is None. The two arguments must be supplied together; this error means a caller violated that contract. It is a programming error, not a runtime condition.
Source
Thrown at crates/agent/src/thread.rs:6454
context,
kind: acp_thread::AuthorizationKind::PermissionGrant,
},
)))
{
log::error!("Failed to send tool call authorization: {error}");
return Err(anyhow!("Failed to send tool call authorization: {error}"));
}
let Some(check_settings) = check_settings else {
let outcome = response_rx
.await
.map_err(|_| anyhow!("authorization channel closed"))?;
ensure_tool_call_authorization_not_interrupted(&outcome)?;
return Self::persist_permission_outcome(&outcome, fs, cx);
};
let Some((auto_allow_outcome, auto_deny_outcome)) = auto_resolution_outcomes else {
return Err(anyhow!("missing auto-resolution outcomes"));
};
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();
})
});
// Race the user's response against settings changes. On each
// settings change, re-evaluate `check_settings`: if it now
// yields a definitive Allow or Deny, resolve the prompt
// without user interaction. Otherwise keep waiting on the
// same prompt.
loop {
let settings_changed = async {
if settings_rx.changed().await.is_err() {
std::future::pending::<()>().await;View on GitHub (pinned to bc538def45)
Solutions
- Fix the call site: pass `auto_resolution_outcomes` whenever `check_settings` is Some.
- Make the invariant unrepresentable: bundle the callback and its outcomes in a single struct passed as one Option.
- Add a debug_assert at function entry so mismatches fail loudly in tests.
Example fix
// before
fn authorize(
check_settings: Option<CheckFn>,
auto_resolution_outcomes: Option<(Outcome, Outcome)>, // can diverge from check_settings
)
// after
struct AutoResolution<CheckFn> {
check: CheckFn,
allow_outcome: Outcome,
deny_outcome: Outcome,
}
fn authorize(auto_resolution: Option<AutoResolution<CheckFn>>) // paired or absent, never half Defensive patterns
Strategy: validation
Validate before calling
// Enforce the pairing at the call boundary:
let (check_settings, auto_resolution_outcomes) = match auto_resolution {
Some(ar) => (Some(ar.check), Some((ar.allow_outcome, ar.deny_outcome))),
None => (None, None),
};
debug_assert_eq!(
check_settings.is_some(),
auto_resolution_outcomes.is_some(),
"check_settings requires auto_resolution_outcomes"
); Prevention
- Bundle the settings callback and its outcomes in one struct so they cannot diverge.
- Add a debug_assert for the invariant at function entry.
- When adding new permission modes, update every caller in the same change.
When it happens
Trigger: Invoking the authorization helper with a `check_settings` callback but `auto_resolution_outcomes: None` — e.g. a new call site enabled settings checking without providing outcomes, or a refactor dropped the tuple while keeping the callback.
Common situations: Refactors of the permission flow; new permission modes added without updating every caller; custom agent-thread setups that hand-roll the arguments.
Related errors
- authorization receiver was dropped
- Failed to send tool call authorization: {error}
- {reason}
- output token limit reached
- message not found
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/5083d18d985bb520.
Report an issue: GitHub.