jdx/mise · error
firewall port '{range}' must be a number or inclusive range
Error message
firewall port '{range}' must be a number or inclusive range What it means
Raised while parsing a `[bootstrap.linux.firewall]` rule's `port` key. The key is an untagged TOML value: either a bare integer (single port) or a string containing an inclusive range with a `-` or `:` separator ("8000-9000", "8000:9000"). This error means a string was supplied but contained no range separator, so it cannot be split into start/end — almost always a single port that was quoted.
Source
Thrown at src/system/firewall.rs:190
#[serde(untagged)]
pub enum FirewallPortToml {
Single(u16),
Range(String),
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FirewallPort {
start: u16,
end: u16,
}
impl FirewallPort {
fn from_toml(value: FirewallPortToml) -> Result<Self> {
let (start, end) = match value {
FirewallPortToml::Single(port) => (port, port),
FirewallPortToml::Range(range) => {
let Some((start, end)) = range.split_once(['-', ':']) else {
bail!("firewall port '{range}' must be a number or inclusive range")
};
(start.parse()?, end.parse()?)
}
};
if start == 0 || end == 0 || start > end {
bail!("firewall port range {start}-{end} is invalid");
}
Ok(Self { start, end })
}
fn contains(self, port: u16) -> bool {
self.start <= port && port <= self.end
}
fn render(self, separator: char) -> String {
if self.start == self.end {
self.start.to_string()
} else {View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Use a bare integer for a single port: `port = 443` (remember `protocol = "tcp"` is required when port is set).
- Use an inclusive range string: `port = "8000-9000"` or `port = "8000:9000"`.
- List each port in its own `[[bootstrap.linux.firewall.rules]]` entry — comma lists are not supported.
- Validate config cheaply with `mise bootstrap firewall status` (it parses the config without applying) before `apply`.
Example fix
# before (mise.toml) [[bootstrap.linux.firewall.rules]] name = "https" port = "443" # after [[bootstrap.linux.firewall.rules]] name = "https" port = 443 protocol = "tcp" # or a range: port = "8000-9000", protocol = "tcp"
Defensive patterns
Strategy: validation
Validate before calling
# pre-flight: quoted single ports and separator-less strings
python3 - <<'PY'
import tomllib
fw = tomllib.load(open('mise.toml','rb')).get('bootstrap',{}).get('linux',{}).get('firewall',{})
for r in fw.get('rules',[]):
p = r.get('port')
if isinstance(p, str) and not any(c in p for c in '-:'):
raise SystemExit(f"rule {r['name']}: port string '{p}' has no range separator; use an integer")
PY Prevention
- Never quote single ports; TOML integers are valid.
- Use only 'start-end' or 'start:end' for ranges; comma lists are unsupported.
- Run `mise bootstrap firewall status` after editing config — it parses without applying.
When it happens
Trigger: `port = "443"` in a rule: the untagged enum first tries `Single(u16)` and fails because the value is a TOML string, then `Range("443")` fails `split_once(['-', ':'])`. Also triggered by free-form strings like "https", "443," or "443 8443" (comma/space lists are unsupported).
Common situations: Quoting numbers out of TOML habit; copying port strings from docker/ufw documentation that uses "443" or "443,8443" forms; YAML-to-TOML migrations that stringified values.
Related errors
- firewall port range {start}-{end} is invalid
- firewall rule '{}' is declared more than once
- firewall rule '{name}' is declared more than once
- 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/47b531058e29276a.
Report an issue: GitHub.