jdx/mise · error

conflicting bootstrap secret declarations for

Error message

conflicting bootstrap secret declarations for {name}

  first:
    {}

  second:
    {}

What it means

Bootstrap secrets are collected from every bootstrap config map and merged by name. If the same secret name is declared twice with different source definitions, mise cannot pick one, so it errors showing both conflicting declarations. Identical duplicates are silently accepted.

Solutions

  1. Remove or rename the duplicate declaration in one of the config files
  2. Make the declarations identical (same env, description, allow_empty) so they merge cleanly
  3. Use `mise bootstrap plan` / inspect loaded config files to find which two files declare the secret

Example fix

# before — global mise.toml
[bootstrap.secrets.API_KEY]
env = "GLOBAL_API_KEY"
# project mise.toml
[bootstrap.secrets.API_KEY]
env = "PROJECT_API_KEY"
# after
[bootstrap.secrets.API_KEY]
env = "PROJECT_API_KEY"
Defensive patterns

Strategy: validation

Validate before calling

// before apply, detect duplicate secret names across loaded configs
let mut seen: HashMap<&str, &str> = HashMap::new();
for (file, decls) in all_bootstrap_config_maps() {
    for (name, d) in decls {
        if let Some(prev) = seen.insert(name, file) {
            eprintln!("duplicate secret {name} in {prev} and {file}");
        }
    }
}

Try / catch

match result {
    Err(e) if e.to_string().contains("conflicting bootstrap secret declarations") => {
        eprintln!("{}", e); // message embeds both declarations
        // inspect the listed config files and deduplicate
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling statuses() or resolve() (during `mise bootstrap` operations) when two loaded config files both declare a secret with the same name but different env/description/allow_empty settings.

Common situations: A global mise.toml and a project mise.toml both define [bootstrap.secret.API_KEY] with different env var names, or an included config redefines an existing secret with tweaked options after copying a template.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/c9b3f241ccefe0ca. Report an issue: GitHub.

Appendix: source

Thrown at src/system/secrets.rs:101

}

#[derive(Debug, thiserror::Error)]
#[error(
    "required bootstrap secrets are unavailable: {details}. Supply them in the environment (for example, `fnox exec -- mise bootstrap ...`) or pass --prompt-secrets"
)]
struct SecretUnavailable {
    details: String,
}

pub(crate) fn declarations_from_config(config: &Config) -> Result<Vec<SecretDeclaration>> {
    let mut merged: IndexMap<String, (SecretDeclaration, ResourceOrigin)> = IndexMap::new();
    for config_files in config.bootstrap_config_maps() {
        for (name, declaration) in secrets_from_config_files(config_files)? {
            if let Some(existing) = merged.get(&name) {
                if existing.0 == declaration.0 {
                    continue;
                }
                bail!(
                    "conflicting bootstrap secret declarations for {name}\n\n  first:\n    {}\n\n  second:\n    {}",
                    existing.1.conflict_description(),
                    declaration.1.conflict_description(),
                );
            }
            merged.insert(name, declaration);
        }
    }
    Ok(merged
        .into_values()
        .map(|(declaration, _)| declaration)
        .collect())
}

fn secrets_from_config_files(
    config_files: &ConfigMap,
) -> Result<IndexMap<String, (SecretDeclaration, ResourceOrigin)>> {
    let mut merged = IndexMap::new();

View on GitHub (pinned to 533346cc37)