jdx/mise · error

refusing unsafe firewall change; inspect `mise bootstrap fir

Error message

refusing unsafe firewall change; inspect `mise bootstrap firewall status`

What it means

A fail-closed safety stop in firewall apply: the computed plan contains at least one resource whose action is `Unknown` — mise could not determine its current state — and the run is not `--dry-run`. mise will not modify a firewall it cannot fully see, so it refuses and points at `mise bootstrap firewall status` for diagnosis. In dry-run mode the same condition only prints 'would not change ... (manual action required)' warnings.

Source

Thrown at src/system/firewall.rs:717

        .count();
    if changes == 0 {
        info!("firewall: already converged");
        return Ok(());
    }
    if firewall_change_is_unsafe(&plan) {
        if dry_run {
            for resource in plan
                .iter()
                .filter(|resource| resource.action == ResourceAction::Unknown)
            {
                warn!(
                    "would not change {}: current {}, desired {} (manual action required)",
                    resource.id, resource.current, resource.desired
                );
            }
            return Ok(());
        }
        bail!("refusing unsafe firewall change; inspect `mise bootstrap firewall status`");
    }
    if dry_run {
        let inspection = request.inspection.as_ref().expect("firewall was inspected");
        let backend = inspection.backend.expect("available firewall backend");
        for command in preview_commands(request, backend)? {
            miseprintln!("would run {}", shell_words::join(command));
        }
        return Ok(());
    }
    let destructive = request.exclusive
        || matches!(
            request.state,
            FirewallState::Disabled | FirewallState::Absent
        )
        || plan
            .iter()
            .any(|resource| resource.action == ResourceAction::Remove);
    if !yes

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Run `mise bootstrap firewall status` to see current vs desired per resource and identify the indeterminate one.
  2. Run `mise bootstrap firewall apply --dry-run` — every 'would not change X ... (manual action required)' line is an item you must reconcile by hand.
  3. Remove or normalize the out-of-band object with the backend's own CLI (`nft delete rule ...`, `firewall-cmd`, `ufw delete`), then re-run apply so inspection sees a known state.
  4. Check backend health and version (`nft --version`, `firewall-cmd --state`, `ufw status`) — a broken backend produces unparseable output.

Example fix

# before
mise bootstrap firewall apply
# error: refusing unsafe firewall change; inspect `mise bootstrap firewall status`

# after — diagnose, reconcile manually, re-run
mise bootstrap firewall status
mise bootstrap firewall apply --dry-run   # lists 'manual action required' items
sudo nft delete rule inet filter unknown-rule-handle-42   # example manual fix
mise bootstrap firewall apply
Defensive patterns

Strategy: try-catch

Validate before calling

# pre-flight: make the unknowns visible without changing anything
mise bootstrap firewall apply --dry-run   # 'manual action required' lines = items to reconcile

Try / catch

if ! mise bootstrap firewall apply 2>fw.err; then
  if grep -q "refusing unsafe firewall change" fw.err; then
    mise bootstrap firewall status        # inspect current vs desired
    exit 1                                 # reconcile manually, then re-run
  fi
  cat fw.err; exit 1
fi

Prevention

When it happens

Trigger: `mise bootstrap firewall apply` where inspection marked some resource's action Unknown: backend command present but its output could not be parsed/mapped, exotic rules created out-of-band by other tools, or firewall state changed between inspect and plan. The bail sits on the non-dry-run path right after the Unknown-warning loop.

Common situations: Firewalls partially managed by other tools (shorewall, cloud host agents, hand-run nft commands) leaving structures the inspection cannot map; version skew between the installed nft/firewalld/ufw output format and mise's parsers; a previously interrupted apply leaving half-created objects.

Related errors


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