{"record":{"id":"0b9ce7e1c42250bb","repo":"jdx/mise","slug":"firewall-rule-name-is-declared-more-than-once","errorCode":null,"errorMessage":"firewall rule '{name}' is declared more than once","messagePattern":"firewall rule '(.+?)' is declared more than once","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/firewall.rs","lineNumber":392,"sourceCode":"            \"--no-hooks\".to_string(),\n            \"bootstrap\".to_string(),\n            \"__inspect-firewall-plan\".to_string(),\n        ],\n        &input,\n    )?;\n    request.inspection = Some(serde_json::from_slice(&output)?);\n    Ok(())\n}\n\nimpl FirewallRequest {\n    fn from_toml(config: FirewallTomlConfig) -> Result<Self> {\n        let mut rules = vec![];\n        let mut names = HashSet::new();\n        for rule in config.rules {\n            let name = rule.name;\n            validate_name(&name)?;\n            if !names.insert(name.clone()) {\n                bail!(\"firewall rule '{name}' is declared more than once\");\n            }\n            let interface = rule\n                .interface\n                .map(|interface| validate_interface(interface.trim()))\n                .transpose()?;\n            let source = rule\n                .source\n                .map(|source| source.parse::<IpNet>())\n                .transpose()\n                .wrap_err_with(|| format!(\"firewall rule '{name}' has an invalid source\"))?;\n            let destination = rule\n                .destination\n                .map(|destination| destination.parse::<IpNet>())\n                .transpose()\n                .wrap_err_with(|| format!(\"firewall rule '{name}' has an invalid destination\"))?;\n            if source.is_some_and(|source| {\n                destination.is_some_and(|destination| {\n                    source.addr().is_ipv4() != destination.addr().is_ipv4()","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/firewall.rs#L374-L410","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make every rule name unique in the effective config: `grep -n 'name =' mise.toml` and deduplicate.","If you meant to override a rule, keep one entry with that name and edit it rather than adding a second.","Choose stable, descriptive names (`ssh-allow`, `web-https`) since the name also identifies the generated backend object."],"exampleFix":"# before\n[[bootstrap.linux.firewall.rules]]\nname = \"ssh\"\nport = 22\nprotocol = \"tcp\"\n\n[[bootstrap.linux.firewall.rules]]\nname = \"ssh\"\nport = 2222\nprotocol = \"tcp\"\n\n# after — one rule per name\n[[bootstrap.linux.firewall.rules]]\nname = \"ssh\"\nport = 22\nprotocol = \"tcp\"\n\n[[bootstrap.linux.firewall.rules]]\nname = \"ssh-alt\"\nport = 2222\nprotocol = \"tcp\"","handlingStrategy":"validation","validationCode":"# pre-flight: unique names in the merged/effective view\npython3 - <<'PY'\nimport tomllib, collections\ncfg = tomllib.load(open('mise.toml','rb'))\nrules = cfg.get('bootstrap',{}).get('linux',{}).get('firewall',{}).get('rules',[])\ndupes = [n for n, c in collections.Counter(r['name'] for r in rules).items() if c > 1]\nif dupes: raise SystemExit(f\"duplicate firewall rule names: {dupes}\")\nPY","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Grep before saving: `grep -n 'name =' mise.toml`.","One concern per rule and one rule per name; edit rather than duplicate.","Remember names are the merge key across layered config files."],"tags":["mise","firewall","toml","duplicate-key","bootstrap","config"],"backgroundTag":"duplicate-config-key","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}