jdx/mise · error

firewall rule '{}' uses action = "limit", which requires dir

Error message

firewall rule '{}' uses action = "limit", which requires direction = "incoming" and protocol = "tcp"

What it means

Rule-level semantic validation: the `limit` action (per-source connection rate limiting) is only expressible for incoming TCP rules. A rule with `action = "limit"` but direction not `incoming` or protocol not `tcp` is rejected before any backend command runs, because no supported backend can translate it correctly.

Source

Thrown at src/system/firewall.rs:1046

                        command_output(
                            "firewall-cmd",
                            &["--permanent", &format!("--info-policy={policy}")],
                        )
                        .is_ok_and(|output| output.status.success())
                    })
        }
        FirewallBackend::Ufw => backend_active(backend),
        FirewallBackend::Auto => false,
    }
}

fn validate_backend_request(request: &FirewallRequest, backend: FirewallBackend) -> Result<()> {
    for rule in &request.rules {
        if rule.action == FirewallAction::Limit {
            if rule.direction != FirewallDirection::Incoming
                || rule.protocol != Some(FirewallProtocol::Tcp)
            {
                bail!(
                    "firewall rule '{}' uses action = \"limit\", which requires direction = \"incoming\" and protocol = \"tcp\"",
                    rule.name
                );
            }
            if backend == FirewallBackend::Firewalld {
                bail!(
                    "firewall rule '{}' uses per-source connection limiting, which firewalld policies cannot express safely; select backend = \"nftables\" or \"ufw\"",
                    rule.name
                );
            }
        }
        if backend == FirewallBackend::Firewalld && rule.interface.is_some() {
            bail!(
                "firewall rule '{}' uses interface matching, which firewalld policies cannot express safely; select backend = \"nftables\" or \"ufw\"",
                rule.name
            );
        }
        if backend == FirewallBackend::Ufw

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Set `direction = "incoming"` and `protocol = "tcp"` on the rule using `action = "limit"`
  2. Or change the action to `allow`/`deny` if you do not actually need rate limiting
  3. Split the intent: keep the limit rule for incoming TCP, define separate allow/deny rules for other protocols/directions

Example fix

// before
[[rule]]
name = "rate-limit-dns"
action = "limit"
direction = "outgoing"
protocol = "udp"
// after
[[rule]]
name = "rate-limit-ssh"
action = "limit"
direction = "incoming"
protocol = "tcp"
Defensive patterns

Strategy: validation

Validate before calling

// pre-check in config lint
function validLimitRule(r) {
  return r.action !== "limit" || (r.direction === "incoming" && r.protocol === "tcp");
}

Prevention

When it happens

Trigger: Defining a firewall rule with `action = "limit"` and either `direction = "outgoing"` or a protocol other than `tcp` (e.g. udp, sctp) in the bootstrap firewall config, then running inspect/apply.

Common situations: Copy-pasting a rate-limited SSH rule template and changing direction/protocol; intending to rate-limit UDP traffic; misremembering that limit applies only to inbound TCP connection throttling.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/89404846466457e0. Report an issue: GitHub.