jdx/mise · error

task executor initialized

Error message

task executor initialized

What it means

In `mise run` setup, Step 5 calls `self.setup_executor()?` and immediately afterwards borrows `self.executor.as_ref().expect("task executor initialized")` to preflight every scheduled invocation (validating tasks/deps before anything executes, src/cli/run.rs:877-894). setup_executor returns Err on failure, so reaching the loop with executor == None means the setup step skipped initialization without erroring — an internal wiring invariant.

Source

Thrown at src/cli/run.rs:883

        self.setup_output_and_validate(&tasks)?;
        self.output = Some(self.output(None));

        // Step 3: Install tools needed by tasks
        if !self.skip_tools {
            self.install_task_tools(&mut config, &tasks, &previewed_tools)
                .await?;
        }

        // Step 4: Bracket action caching with this top-level task run. The
        // session owns the local agent and is flushed before results report.
        self.setup_cache_session(&tasks).await?;

        // Step 5: Create TaskExecutor after tool installation
        self.setup_executor()?;

        // Validate every scheduled invocation before starting the scheduler so
        // an invalid parent or dependency cannot run any task commands first.
        let executor = self.executor.as_ref().expect("task executor initialized");
        for task in tasks.all() {
            if let Err(err) = executor
                .preflight_task_usage(&config, task)
                .await
                .wrap_err_with(|| format!("failed to validate task {}", task.name))
            {
                if let Some(session) = &self.cache_session
                    && let Err(finish_err) = session.finish().await
                {
                    warn!("failed to finish action cache session: {finish_err:#}");
                }
                return Err(err);
            }
        }

        // Disable exit-on-ctrl-c so tasks can handle SIGINT gracefully
        ctrlc::exit_on_ctrl_c(false);

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. If hit as a user: update/pin a released mise build — the run pipeline in that build is broken
  2. As a contributor: make setup_executor unconditionally store Some(executor) or return Err; never return Ok with the field unset
  3. Add a debug_assert!(self.executor.is_some()) at the end of setup_executor so regressions fail fast in debug builds
  4. Report at https://github.com/jdx/mise/issues with the panic backtrace and `mise run` invocation

Example fix

// before: setup can silently skip
fn setup_executor(&mut self) -> Result<()> {
    if self.tasks.is_empty() { return Ok(()); } // executor stays None -> later panic
    self.executor = Some(TaskExecutor::new(...));
    Ok(())
}

// after: always initialize or fail
fn setup_executor(&mut self) -> Result<()> {
    self.executor = Some(TaskExecutor::new(...)?);
    Ok(())
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: A refactor that makes setup_executor conditionally leave executor as None (returning Ok), or a new code path that reaches the preflight loop without calling setup_executor. The normal `mise run` flow (including --dry-run and cache sessions) always initializes the executor or aborts with the setup error.

Common situations: Contributors adding new run modes that bypass executor setup; broken builds. Users cannot trigger it via task configs — bad task configs surface in the preflight itself as 'failed to validate task <name>' errors, not this panic.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/508ed8f41defbe5f. Report an issue: GitHub.