jdx/mise · error

refusing unsafe change to bootstrap compose project '{}'; in

Error message

refusing unsafe change to bootstrap compose project '{}'; inspect `mise bootstrap plan`

What it means

`apply_with_dry_run_actions` reconciles each `[bootstrap.compose.<name>]` request against the actions recorded during dry-run/plan, refined by a fresh classification in `apply_action`. A project classified as `ResourceAction::Unknown` means mise cannot safely determine whether the deployed project matches the config, and it refuses to guess rather than risk tearing down or recreating a live project. The message points at `mise bootstrap plan` for inspection.

Source

Thrown at src/system/compose.rs:209

        .map(|request| request.plan_with_dependency_change(dependency_changed))
        .collect()
}

pub fn apply(requests: &[ComposeRequest], dry_run: bool, yes: bool) -> Result<()> {
    apply_with_dry_run_actions(requests, &HashMap::new(), dry_run, yes)
}

pub fn apply_with_dry_run_actions(
    requests: &[ComposeRequest],
    dry_run_actions: &HashMap<String, ResourceAction>,
    dry_run: bool,
    yes: bool,
) -> Result<()> {
    let mut changes = vec![];
    for request in requests {
        let action = apply_action(request, dry_run_actions, dry_run);
        match action {
            ResourceAction::Unknown => bail!(
                "refusing unsafe change to bootstrap compose project '{}'; inspect `mise bootstrap plan`",
                request.name
            ),
            ResourceAction::Noop => {}
            ResourceAction::Create | ResourceAction::Update | ResourceAction::Remove => {
                changes.push(request)
            }
        }
    }
    if changes.is_empty() {
        info!("compose projects: already converged");
        return Ok(());
    }
    if dry_run {
        for request in changes {
            for argv in request.dry_run_action_argvs()? {
                miseprintln!("would run {}", shell_words::join(argv));
            }

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Run `mise bootstrap plan` (or `apply --dry-run`) and read the reported action for the named project — Unknown there shows classification itself failed.
  2. Fix the classification blocker: ensure the docker engine is reachable (and reachable under sudo if `sudo = true`), compose v2 works, and `project_dir`/`files` resolve.
  3. Re-run `mise bootstrap compose apply`; once every project classifies as Noop/Create/Update/Remove the refusal disappears.

Example fix

# before
mise bootstrap compose apply
# error: refusing unsafe change to bootstrap compose project 'web'; ...

# after
mise bootstrap compose plan     # inspect why the project classifies as Unknown
# fix engine/compose availability, then:
mise bootstrap compose apply
Defensive patterns

Strategy: validation

Validate before calling

// run the plan pass first and assert every project has a known action
let plans = compose::plans(&requests);
for plan in &plans {
    assert!(
        !matches!(plan.action, ResourceAction::Unknown),
        "project {} classifies as Unknown; fix engine/compose availability first",
        plan.id.value
    );
}
compose::apply_with_dry_run_actions(&requests, &dry_run_actions, dry_run, yes)?;

Try / catch

match compose::apply_with_dry_run_actions(&reqs, &actions, dry_run, yes) {
    Ok(()) => {}
    Err(err) if err.to_string().contains("refusing unsafe change") => {
        // do NOT force past it: re-run `mise bootstrap compose plan`, fix why the named
        // project classifies as Unknown (engine reachable? sudo context?), then retry.
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Running apply when the dry-run/plan map has no usable entry for a project, or when live classification cannot decide (docker engine unreachable mid-run, `docker compose config` failing, state indeterminate) so `apply_action` falls back to Unknown.

Common situations: Applying right after a plan made on a different host or with the engine down; engine socket permissions differing under sudo vs the user; stale plan data after the compose file changed underneath the run.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/e75aa98195c708e8. Report an issue: GitHub.