jdx/mise · error

new config Arc is uniquely owned

Error message

new config Arc is uniquely owned

What it means

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.

Solutions

  1. Ensure the config Arc returned by the parse step is not cloned or stored anywhere before `Arc::get_mut` runs
  2. If shared ownership is genuinely needed, wrap the mutated fields in `Arc::make_mut`/`RwLock` instead of relying on uniqueness
  3. Re-run the dry-run config build so a brand-new Arc is produced immediately before mutation
  4. Add a debug assertion or log near the parse call to catch accidental clones in development

Example fix

// before
let config = self.reparse_for_dry_run(&files)?;
let shared = config.clone(); // leaks a reference
let config_mut = Arc::get_mut(&mut config).expect("new config Arc is uniquely owned");
// after
let config = self.reparse_for_dry_run(&files)?;
// no clones before mutation
let config_mut = Arc::get_mut(&mut config).expect("new config Arc is uniquely owned");
Defensive patterns

Strategy: validation

Validate before calling

assert_eq!(Arc::strong_count(&config), 1, "config Arc must be uniquely owned before mutation");

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/c3f0d35822e71087. Report an issue: GitHub.

Appendix: source

Thrown at src/config/mod.rs:486

    ) -> Result<Arc<Self>> {
        let mut config = self.with_config_files(config_files);
        let bootstrap_roots = self
            .bootstrap_config_maps
            .iter()
            .filter_map(|map| map.config_root.as_deref())
            .collect_vec();
        let mut main_config_files = config.config_files.clone();
        main_config_files
            .retain(|path, _| !bootstrap_roots.iter().any(|root| path.starts_with(root)));
        let vars = bootstrap_dry_run_vars(
            Some(&self.config_files),
            self.vars_results_cached(),
            &main_config_files,
            IndexMap::new(),
            false,
        )?;
        let main_vars_changed = vars != self.vars;
        let config_mut = Arc::get_mut(&mut config).expect("new config Arc is uniquely owned");
        config_mut.vars = vars.clone();
        config_mut.tera_ctx.insert("vars", &vars);
        for map in &mut config_mut.bootstrap_config_maps {
            let original_config_files = map.config_files.clone();
            for (path, simulated) in &config_mut.config_files {
                let belongs_to_map = match &map.config_root {
                    Some(root) => path.starts_with(root),
                    None => !bootstrap_roots.iter().any(|root| path.starts_with(root)),
                };
                if belongs_to_map {
                    insert_bootstrap_dry_run_config_file(
                        &mut map.config_files,
                        path.clone(),
                        simulated.clone(),
                    );
                }
            }
            if map.config_root.is_some() {

View on GitHub (pinned to afd2eddd3a)