netbirdio/netbird · error
invalid rule format: expected protocol/port or protocol/port
Error message
invalid rule format: expected protocol/port or protocol/port-range
What it means
ParseRuleString only accepts the bare keywords 'all' and 'icmp' or strings of the form protocol/port with exactly one '/' separator. It splits on '/' and requires exactly two segments; anything else fails here before protocol or port parsing begins.
Source
Thrown at shared/management/types/policy.go:199
for groupID := range groups {
groupIDs = append(groupIDs, groupID)
}
return groupIDs
}
func ParseRuleString(rule string) (PolicyRuleProtocolType, RulePortRange, error) {
rule = strings.TrimSpace(strings.ToLower(rule))
if rule == "all" {
return PolicyRuleProtocolALL, RulePortRange{}, nil
}
if rule == "icmp" {
return PolicyRuleProtocolICMP, RulePortRange{}, nil
}
split := strings.Split(rule, "/")
if len(split) != 2 {
return "", RulePortRange{}, errors.New("invalid rule format: expected protocol/port or protocol/port-range")
}
protoStr := strings.TrimSpace(split[0])
portStr := strings.TrimSpace(split[1])
var protocol PolicyRuleProtocolType
switch protoStr {
case "tcp":
protocol = PolicyRuleProtocolTCP
case "udp":
protocol = PolicyRuleProtocolUDP
case "icmp":
return "", RulePortRange{}, errors.New("icmp does not accept ports; use 'icmp' without '/…'")
case "netbird-ssh":
return PolicyRuleProtocolNetbirdSSH, RulePortRange{Start: nativeSSHPortNumber, End: nativeSSHPortNumber}, nil
default:
return "", RulePortRange{}, fmt.Errorf("invalid protocol: %q", protoStr)
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Format rules as protocol/port, e.g. tcp/22, or a range like udp/5000-5010
- Use the bare keywords 'all' or 'icmp' when no port applies
- Normalize separators and trim whitespace before submitting rules
Example fix
// before
proto, ports, err := types.ParseRuleString("tcp 80")
// after
proto, ports, err := types.ParseRuleString("tcp/80") Defensive patterns
Strategy: validation
Validate before calling
var ruleRe = regexp.MustCompile(`^(all|icmp|[a-z-]+/[0-9]+(-[0-9]+)?)$`)
if !ruleRe.MatchString(strings.ToLower(strings.TrimSpace(rule))) {
return fmt.Errorf("rule %q must be 'all', 'icmp', or protocol/port[-range]", rule)
} Type guard
func isParsableRule(rule string) bool {
r := strings.ToLower(strings.TrimSpace(rule))
if r == "all" || r == "icmp" {
return true
}
parts := strings.Split(r, "/")
return len(parts) == 2 && parts[0] != "" && parts[1] != ""
} Try / catch
if _, _, err := types.ParseRuleString(rule); err != nil {
return fmt.Errorf("rejecting rule %q from import: %w", rule, err)
} Prevention
- Validate rule strings at the input boundary (UI, import parser)
- Use one canonical producer for rule strings instead of formatting at several call sites
- Reject wrong separators like ':' early with a clear message
When it happens
Trigger: Inputs like "tcp" (no port), "tcp/80/443" (two slashes), "80" (port only), "tcp:80" (wrong separator), or an empty/whitespace-only string.
Common situations: Hand-written access-control rule strings in policy payloads; CSV/YAML imports with malformed cells; free-text UI fields without pre-validation.
Related errors
- icmp does not accept ports; use 'icmp' without '/…'
- empty port
- invalid duration format: %v
- no keys found in bundle
- failed to decode PEM data
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/17053c5d676c1c81.
Report an issue: GitHub.