{"record":{"id":"20ac484a10755f54","repo":"jdx/mise","slug":"plugin-task-name-should-be-registered-before-spawn","errorCode":null,"errorMessage":"plugin task name should be registered before spawning","messagePattern":"plugin task name should be registered before spawning","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli/plugins/mod.rs","lineNumber":24,"sourceCode":"use eyre::{Report, Result, WrapErr, eyre};\nuse tokio::task::{Id, JoinSet};\n\nuse crate::config::Config;\n\npub(crate) mod install;\nmod link;\nmod ls;\nmod ls_remote;\nmod uninstall;\nmod update;\n\ntype PluginTaskResult = Result<()>;\ntype PluginTaskNames = HashMap<Id, String>;\n\nfn take_plugin_name(task_names: &mut PluginTaskNames, id: Id) -> String {\n    task_names\n        .remove(&id)\n        .expect(\"plugin task name should be registered before spawning\")\n}\n\nfn spawn_plugin_task<F>(\n    tasks: &mut JoinSet<PluginTaskResult>,\n    task_names: &mut PluginTaskNames,\n    plugin: impl Into<String>,\n    task: F,\n) where\n    F: Future<Output = PluginTaskResult> + Send + 'static,\n{\n    let task = tasks.spawn(task);\n    task_names.insert(task.id(), plugin.into());\n}\n\nasync fn join_plugin_tasks(\n    mut tasks: JoinSet<PluginTaskResult>,\n    mut task_names: PluginTaskNames,\n    operation: &'static str,","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/cli/plugins/mod.rs#L6-L42","documentation":"mise runs plugin operations (install/update/uninstall across many plugins) concurrently in a tokio JoinSet, tracking a HashMap<Id, String> from spawned task id to plugin name. take_plugin_name removes and returns the name for a finished task and expects it to have been registered by spawn_plugin_task (src/cli/plugins/mod.rs:21-37). The expect fires if a completed task's id is missing from the map — bookkeeping between spawns and joined results diverged.","triggerScenarios":"Only a code change that polls the JoinSet without going through spawn_plugin_task's registration, or that takes a name twice for the same id; current code registers on every spawn and consumes each joined id exactly once (success, error, and panic/JoinError paths all route through take_plugin_name). Users running `mise plugins update|install|uninstall` in a correctly built mise cannot trigger it.","commonSituations":"Contributors adding new parallel plugin flows that call tasks.join_next_with_id() directly or forget task_names insertion; broken builds. The module's own tests (panicked_plugin_task_does_not_cancel_other_tasks) pin the intended behavior.","solutions":["If hit as a user: update mise — internal concurrency bookkeeping bug; retry the plugin operation once after the panic since plugin state may be partially applied","As a contributor: always spawn via spawn_plugin_task so the id is registered, and never consume JoinSet results outside join_plugin_tasks","Add a regression test with a mix of panicking and succeeding tasks (see the existing tests in src/cli/plugins/mod.rs)","Report at https://github.com/jdx/mise/issues with the panic backtrace"],"exampleFix":"// before: spawning directly, id never registered\ntasks.spawn(install_plugin(plugin.clone()));\n// later: take_plugin_name(&mut task_names, id) -> panic\n\n// after: route through the helper\nspawn_plugin_task(&mut tasks, &mut task_names, plugin.clone(), install_plugin(plugin));","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Retry the plugin operation after a panic — plugin state may be partially applied, and mise plugins ls shows current state","Contributors: always spawn plugin work through spawn_plugin_task so ids are registered","Pin released versions; report backtraces upstream"],"tags":["plugins","concurrency","invariant","panic","internal"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}