zed-industries/zed · error

Only call run once

Error message

Only call run once

What it means

Contract guard in AskPass::run: the askpass master process and channels are stored as Options that run() takes (via take/replace semantics), so calling run() a second time would find them already consumed. Firing means a caller reused an AskPass instance after its first run.

Source

Thrown at crates/askpass/src/askpass.rs:193

            askpass_task,
            askpass_kill_master_rx: Some(askpass_kill_master_rx),
            askpass_opened_rx: Some(askpass_opened_rx),
            executor,
        })
    }

    // This will run the askpass task forever, resolving as many authentication requests as needed.
    // The caller is responsible for examining the result of their own commands and cancelling this
    // future when this is no longer needed. Note that this can only be called once, but due to the
    // drop order this takes an &mut, so you can `drop()` it after you're done with the master process.
    //
    // When `timeout` is provided, this resolves with `AskPassResult::Timedout` if no askpass prompt
    // has been opened within that duration. This is intended for connection establishment (e.g.
    // SSH), where "no prompt and no connection" indicates an unreachable host. Callers wrapping
    // commands that may legitimately run for a long time without prompting (e.g. git) should pass
    // `None` and rely on the command's own completion instead.
    pub async fn run(&mut self, timeout: Option<Duration>) -> AskPassResult {
        let askpass_opened_rx = self.askpass_opened_rx.take().expect("Only call run once");
        let askpass_kill_master_rx = self
            .askpass_kill_master_rx
            .take()
            .expect("Only call run once");
        let executor = self.executor.clone();
        let timer = async move {
            match timeout {
                Some(timeout) => executor.timer(timeout).await,
                None => std::future::pending().await,
            }
        };

        select_biased! {
            _ = askpass_opened_rx.fuse() => {
                // Note: this await can only resolve after we are dropped.
                askpass_kill_master_rx.await.ok();
                AskPassResult::CancelledByUser
            }

View on GitHub (pinned to f4178619ac)

Solutions

  1. Create a new AskPass instance for each authentication session
  2. Call run() exactly once per instance and drop the instance when finished
Defensive patterns

Strategy: type-guard

When it happens

Trigger: Thrown at crates/askpass/src/askpass.rs:193 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/68175f4ec77c88d9. Report an issue: GitHub.