{"record":{"id":"75a010959d3fea09","repo":"jdx/mise","slug":"executor-must-be-initialized-before-displaying-cac","errorCode":null,"errorMessage":"executor must be initialized before displaying cache stats","messagePattern":"executor must be initialized before displaying cache stats","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli/run.rs","lineNumber":1439,"sourceCode":"            } else {\n                bail!(\n                    \"`{dp}` is not executable. {}\",\n                    file::make_executable_hint(path)\n                )\n            }\n        }\n        Ok(())\n    }\n\n    fn timings(&self) -> bool {\n        !self.quiet(None) && !self.no_timings\n    }\n\n    fn display_task_cache_stats(&self) {\n        let stats = *self\n            .executor\n            .as_ref()\n            .expect(\"executor must be initialized before displaying cache stats\")\n            .cache_stats\n            .lock()\n            .unwrap();\n        let lookups = stats.hits.saturating_add(stats.misses);\n        if lookups == 0 {\n            safe_eprintln!(\"Task cache: no lookups\");\n            return;\n        }\n        let hit_rate = stats.hits.saturating_mul(100) / lookups;\n        safe_eprintln!(\n            \"Task cache: {}/{} hits ({}%), {} restored, {} saved\",\n            stats.hits,\n            lookups,\n            hit_rate,\n            ByteSize::b(stats.restored_bytes).display().iec(),\n            crate::ui::time::format_duration(stats.time_saved),\n        );\n    }","sourceCodeStart":1421,"sourceCodeEnd":1457,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/cli/run.rs#L1421-L1457","documentation":"After a task run, mise prints cache hit/miss statistics from the executor's `cache_stats` mutex. This `.expect()` panics if `display_task_cache_stats` is called without an initialized executor. It is an invariant guard ensuring stats display only happens on code paths where tasks actually ran.","triggerScenarios":"Calling `display_task_cache_stats()` after a run that failed before `setup_executor()`; invoking the method from a new code path (e.g. a stats subcommand) that never created the executor; an executor-initialization error being swallowed upstream.","commonSituations":"Developer adds cache-stats output to a new CLI path; an aborted run (e.g. tool install failure) still reaches the stats display in modified code.","solutions":["Report the panic with `MISE_DEBUG=1` output and the invoking command to the mise repo","Guard the stats display so it is skipped when the run never reached executor setup","Check upstream error handling: ensure setup failures propagate instead of falling through to display","Rebuild/update mise to rule out a stale binary"],"exampleFix":"// before\nlet stats = *self.executor.as_ref().expect(\"executor must be initialized before displaying cache stats\").cache_stats.lock().unwrap();\n// after\nlet Some(executor) = self.executor.as_ref() else { return; };\nlet stats = *executor.cache_stats.lock().unwrap();","handlingStrategy":"validation","validationCode":"# Only inspect cache stats after a successful run\nmise run <task> && echo \"run ok\"  # stats printing is internal; never invoke display paths directly","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not call internal runner methods from external tooling","Treat any occurrence as a mise bug and report it","Update mise regularly so internal refactors land intact"],"tags":["rust","panic","internal-invariant","task-cache"],"backgroundTag":"internal-invariant-violation","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}