jdx/mise · error

executor must be initialized before displaying cache stats

Error message

executor must be initialized before displaying cache stats

What it means

After a `mise run` that used the task action cache, mise prints hit/miss statistics by locking `executor.cache_stats`; the executor is obtained via `self.executor.as_ref().expect("executor must be initialized before displaying cache stats")` (src/cli/run.rs:1404-1412). display_task_cache_stats is called from the run flow after setup_executor has run, so the expect guards against the call site drifting to paths where no executor exists (e.g. non-run commands).

Source

Thrown at src/cli/run.rs:1409

            } else {
                bail!(
                    "`{dp}` is not executable. {}",
                    file::make_executable_hint(path)
                )
            }
        }
        Ok(())
    }

    fn timings(&self) -> bool {
        !self.quiet(None) && !self.no_timings
    }

    fn display_task_cache_stats(&self) {
        let stats = *self
            .executor
            .as_ref()
            .expect("executor must be initialized before displaying cache stats")
            .cache_stats
            .lock()
            .unwrap();
        let lookups = stats.hits.saturating_add(stats.misses);
        if lookups == 0 {
            safe_eprintln!("Task cache: no lookups");
            return;
        }
        let hit_rate = stats.hits.saturating_mul(100) / lookups;
        safe_eprintln!(
            "Task cache: {}/{} hits ({}%), {} restored, {} saved",
            stats.hits,
            lookups,
            hit_rate,
            ByteSize::b(stats.restored_bytes).display().iec(),
            crate::ui::time::format_duration(stats.time_saved),
        );
    }

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. If hit: update mise — display of cache stats ran without an initialized run pipeline
  2. As a contributor: gate the call with `if let Some(executor) = &self.executor { ... }` or move it strictly after setup_executor
  3. Consider storing cache stats outside the executor if they must outlive it
  4. Report at https://github.com/jdx/mise/issues

Example fix

// before: unconditional call on a maybe-absent executor
fn display_task_cache_stats(&self) {
    let stats = *self.executor.as_ref().expect("...").cache_stats.lock().unwrap();
}

// after: tolerate absence
fn display_task_cache_stats(&self) {
    let Some(executor) = &self.executor else { return; };
    let stats = *executor.cache_stats.lock().unwrap();
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: A refactor that calls display_task_cache_stats from a context where the run pipeline was not entered (setup_executor skipped or failed silently), or that caches stats display across invocations. The normal path — `mise run <task>` with cache lookups — always has Some(executor) by the time stats are printed.

Common situations: Contributors sharing the stats printer with other commands or moving it earlier in the flow; broken builds. Users see 'Task cache: N/M hits (...)' normally; this panic means the printer ran executor-less.

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/75a010959d3fea09. Report an issue: GitHub.