rust-lang/cargo · error

already loaded config values

Error message

already loaded config values

What it means

This panic is in GlobalContext::values_mut(), which cargo vendor uses to mutate loaded config values. The method first calls self.values() (which lazy-loads config from disk via a OnceCell), then calls .get_mut() on that cell expecting it to now be filled. If the OnceCell's internal state is inconsistent — filled() reported true inside values() but get_mut() returns None — the expect fires.

Source

Thrown at src/context/mod.rs:657

    /// Gets all config values from disk.
    ///
    /// This will lazy-load the values as necessary. Callers are responsible
    /// for checking environment variables. Callers outside of the `config`
    /// module should avoid using this.
    pub fn values(&self) -> CargoResult<&HashMap<String, ConfigValue>> {
        self.values.try_borrow_with(|| self.load_values())
    }

    /// Gets a mutable copy of the on-disk config values.
    ///
    /// This requires the config values to already have been loaded. This
    /// currently only exists for `cargo vendor` to remove the `source`
    /// entries. This doesn't respect environment variables. You should avoid
    /// using this if possible.
    pub fn values_mut(&mut self) -> CargoResult<&mut HashMap<String, ConfigValue>> {
        let _ = self.values()?;
        Ok(self.values.get_mut().expect("already loaded config values"))
    }

    // Note: this is used by RLS, not Cargo.
    pub fn set_values(&self, values: HashMap<String, ConfigValue>) -> CargoResult<()> {
        if self.values.get().is_some() {
            bail!("config values already found")
        }
        match self.values.set(values.into()) {
            Ok(()) => Ok(()),
            Err(_) => bail!("could not fill values"),
        }
    }

    /// Sets the path where ancestor config file searching will stop. The
    /// given path is included, but its ancestors are not.
    pub fn set_search_stop_path<P: Into<PathBuf>>(&mut self, path: P) {
        let path = path.into();
        debug_assert!(self.cwd.starts_with(&path));

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Call ctx.values() explicitly first to ensure config is loaded before calling values_mut().
  2. Check that config files are valid TOML and readable before attempting mutation.
  3. Avoid calling values_mut() from multiple threads — it requires &mut self so this should be impossible, but verify no unsafe aliasing exists in library embedding.
Defensive patterns

Strategy: validation

Validate before calling

// Ensure values are loaded before calling values_mut()
let _ = ctx.values()?; // triggers lazy load
// now safe to mutate
let values = ctx.values_mut()?;

Prevention

When it happens

Trigger: Calling values_mut() without having previously called values() or load_values() successfully, or in a context where the OnceCell was reset/cleared between the borrow check and the get_mut call. In practice this path is only exercised by cargo vendor when stripping [source] entries.

Common situations: Running cargo vendor with a manually corrupted or partially-written config file; using cargo as a library and calling values_mut() after set_values() failed; a re-entrant config access pattern that resets the cell.

Related errors


AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06). Data as JSON: /data/errors/0e23def644f66ad2.json. Report an issue: GitHub.