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
- Remove or rename the duplicate declaration in one of the config files
- Make the declarations identical (same env, description, allow_empty) so they merge cleanly
- 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
- Keep each secret declared in exactly one config layer
- When including/copying config templates, strip duplicated [bootstrap.secrets] tables
- Use `mise bootstrap plan` to review merged secret declarations before apply
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
- conflicting bootstrap service declarations for
- [bootstrap].config_roots did not match any config roots
- bootstrap resource ' ' depends on missing resource
- bootstrap resource ' ' is declared more than once
- bootstrap secret ' ' has invalid environment variable name
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)