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
- 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.
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
- 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.
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
- firewall rule '{}' is declared more than once
- firewall port '{range}' must be a number or inclusive range
- firewall port range {start}-{end} is invalid
- firewall rule '{name}' mixes IPv4 and IPv6 source/destinatio
- firewall rule '{name}' sets port without protocol
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/0b9ce7e1c42250bb.
Report an issue: GitHub.