{"record":{"id":"c3f0d35822e71087","repo":"jdx/mise","slug":"new-config-arc-is-uniquely-owned","errorCode":null,"errorMessage":"new config Arc is uniquely owned","messagePattern":"new config Arc is uniquely owned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/config/mod.rs","lineNumber":486,"sourceCode":"    ) -> Result<Arc<Self>> {\n        let mut config = self.with_config_files(config_files);\n        let bootstrap_roots = self\n            .bootstrap_config_maps\n            .iter()\n            .filter_map(|map| map.config_root.as_deref())\n            .collect_vec();\n        let mut main_config_files = config.config_files.clone();\n        main_config_files\n            .retain(|path, _| !bootstrap_roots.iter().any(|root| path.starts_with(root)));\n        let vars = bootstrap_dry_run_vars(\n            Some(&self.config_files),\n            self.vars_results_cached(),\n            &main_config_files,\n            IndexMap::new(),\n            false,\n        )?;\n        let main_vars_changed = vars != self.vars;\n        let config_mut = Arc::get_mut(&mut config).expect(\"new config Arc is uniquely owned\");\n        config_mut.vars = vars.clone();\n        config_mut.tera_ctx.insert(\"vars\", &vars);\n        for map in &mut config_mut.bootstrap_config_maps {\n            let original_config_files = map.config_files.clone();\n            for (path, simulated) in &config_mut.config_files {\n                let belongs_to_map = match &map.config_root {\n                    Some(root) => path.starts_with(root),\n                    None => !bootstrap_roots.iter().any(|root| path.starts_with(root)),\n                };\n                if belongs_to_map {\n                    insert_bootstrap_dry_run_config_file(\n                        &mut map.config_files,\n                        path.clone(),\n                        simulated.clone(),\n                    );\n                }\n            }\n            if map.config_root.is_some() {","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/config/mod.rs#L468-L504","documentation":"mise panics with 'new config Arc is uniquely owned' when `Arc::get_mut(&mut config)` returns `None` in `with_bootstrap_dry_run_config_files`. The function builds a fresh config `Arc` via `from_dir`-style re-parse and then mutates it in place (`vars`, `tera_ctx`, bootstrap config maps). This is only legal while the new Arc has refcount 1; if anything else cloned the Arc, the mutation is impossible and the invariant is broken.","triggerScenarios":"Calling `with_bootstrap_dry_run_config_files` when the freshly parsed config `Arc` was shared/cloned before reaching `Arc::get_mut` — e.g. a refactor stashes a clone of the new config (in a cache, guard, or closure) before the mutation block, or the config construction path returns an Arc that aliases an existing cached config.","commonSituations":"A developer introduces caching of the newly built config or holds a clone across the re-parse call; concurrent access to the config while the dry-run variant mutates it; changes to the config-load path that return a shared singleton instead of a fresh Arc.","solutions":["Ensure the config Arc returned by the parse step is not cloned or stored anywhere before `Arc::get_mut` runs","If shared ownership is genuinely needed, wrap the mutated fields in `Arc::make_mut`/`RwLock` instead of relying on uniqueness","Re-run the dry-run config build so a brand-new Arc is produced immediately before mutation","Add a debug assertion or log near the parse call to catch accidental clones in development"],"exampleFix":"// before\nlet config = self.reparse_for_dry_run(&files)?;\nlet shared = config.clone(); // leaks a reference\nlet config_mut = Arc::get_mut(&mut config).expect(\"new config Arc is uniquely owned\");\n// after\nlet config = self.reparse_for_dry_run(&files)?;\n// no clones before mutation\nlet config_mut = Arc::get_mut(&mut config).expect(\"new config Arc is uniquely owned\");","handlingStrategy":"validation","validationCode":"assert_eq!(Arc::strong_count(&config), 1, \"config Arc must be uniquely owned before mutation\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not clone the freshly built config Arc before the dry-run mutation block","Prefer Arc::make_mut when shared ownership is possible","Keep the parse and mutate steps adjacent with no intermediate storage"],"tags":["rust","arc","panic","invariant","config"],"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-14T05:17:10.506Z"}