jdx/mise · error

firewall rule '{name}' is declared more than once

Error message

firewall rule '{name}' is declared more than once

What it means

The same uniqueness constraint enforced on the final ruleset when `FirewallRequest::from_toml` builds its rule list: every rule name in the effective (merged) `FirewallTomlConfig` must be unique, checked right after `validate_name`. It fires when the config handed to the request builder contains a repeated literal name — including paths that did not pass through the local-file duplicate check first.

Source

Thrown at src/system/firewall.rs:392

            "--no-hooks".to_string(),
            "bootstrap".to_string(),
            "__inspect-firewall-plan".to_string(),
        ],
        &input,
    )?;
    request.inspection = Some(serde_json::from_slice(&output)?);
    Ok(())
}

impl FirewallRequest {
    fn from_toml(config: FirewallTomlConfig) -> Result<Self> {
        let mut rules = vec![];
        let mut names = HashSet::new();
        for rule in config.rules {
            let name = rule.name;
            validate_name(&name)?;
            if !names.insert(name.clone()) {
                bail!("firewall rule '{name}' is declared more than once");
            }
            let interface = rule
                .interface
                .map(|interface| validate_interface(interface.trim()))
                .transpose()?;
            let source = rule
                .source
                .map(|source| source.parse::<IpNet>())
                .transpose()
                .wrap_err_with(|| format!("firewall rule '{name}' has an invalid source"))?;
            let destination = rule
                .destination
                .map(|destination| destination.parse::<IpNet>())
                .transpose()
                .wrap_err_with(|| format!("firewall rule '{name}' has an invalid destination"))?;
            if source.is_some_and(|source| {
                destination.is_some_and(|destination| {
                    source.addr().is_ipv4() != destination.addr().is_ipv4()

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Make every rule name unique in the effective config: `grep -n 'name =' mise.toml` and deduplicate.
  2. If you meant to override a rule, keep one entry with that name and edit it rather than adding a second.
  3. Choose stable, descriptive names (`ssh-allow`, `web-https`) since the name also identifies the generated backend object.

Example fix

# before
[[bootstrap.linux.firewall.rules]]
name = "ssh"
port = 22
protocol = "tcp"

[[bootstrap.linux.firewall.rules]]
name = "ssh"
port = 2222
protocol = "tcp"

# after — one rule per name
[[bootstrap.linux.firewall.rules]]
name = "ssh"
port = 22
protocol = "tcp"

[[bootstrap.linux.firewall.rules]]
name = "ssh-alt"
port = 2222
protocol = "tcp"
Defensive patterns

Strategy: validation

Validate before calling

# pre-flight: unique names in the merged/effective view
python3 - <<'PY'
import tomllib, collections
cfg = tomllib.load(open('mise.toml','rb'))
rules = cfg.get('bootstrap',{}).get('linux',{}).get('firewall',{}).get('rules',[])
dupes = [n for n, c in collections.Counter(r['name'] for r in rules).items() if c > 1]
if dupes: raise SystemExit(f"duplicate firewall rule names: {dupes}")
PY

Prevention

When it happens

Trigger: A mise.toml whose `[[bootstrap.linux.firewall.rules]]` list contains two entries with the same `name` when `request_from_config` builds the request (e.g. single-file configs or assembled configs where only the merged view is validated); also defensive coverage for programmatic config assembly.

Common situations: Authoring one big mise.toml with many rules and reusing a name; scripts that generate firewall rules appending instead of replacing; refactoring rules and accidentally keeping the old name.

Related errors


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