netbirdio/netbird · error
parse IP %s
Error message
parse IP %s
What it means
While deleting a peer rule, net.ParseIP of the Rule's stored ip string returned nil, so the string is not a valid IPv4 or IPv6 literal. In normal operation ip is produced by ip.String() and always parses; a failure therefore means the Rule was constructed outside the manager with a corrupted, empty, or malformed address (e.g. a CIDR like 10.0.0.1/32 instead of a bare IP).
Source
Thrown at client/firewall/iptables/acl_linux.go:202
m.updateState()
return []firewall.Rule{rule}, nil
}
// DeletePeerRule from the firewall by rule definition
func (m *aclManager) DeletePeerRule(rule firewall.Rule) error {
r, ok := rule.(*Rule)
if !ok {
return fmt.Errorf("invalid rule type")
}
shouldDestroyIpset := false
if ipsetList, ok := m.ipsetStore.ipset(r.ipsetName); ok {
// delete IP from ruleset IPs list and ipset
if _, ok := ipsetList.ips[r.ip]; ok {
ip := net.ParseIP(r.ip)
if ip == nil {
return fmt.Errorf("parse IP %s", r.ip)
}
if err := m.delFromIPSet(r.ipsetName, ip); err != nil {
return fmt.Errorf("delete ip from ipset: %w", err)
}
delete(ipsetList.ips, r.ip)
}
// if after delete, set still contains other IPs,
// no need to delete firewall rule and we should exit here
if len(ipsetList.ips) != 0 {
return nil
}
// we delete last IP from the set, that means we need to delete
// set itself and associated firewall rule too
m.ipsetStore.deleteIpset(r.ipsetName)
shouldDestroyIpset = true
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Always create rules through AddPeerFiltering so ip is set from net.IP.String().
- Validate the field at construction: reject anything net.ParseIP cannot parse, and store bare addresses, never CIDRs.
- If restoring rules from persisted state, re-parse with netip.ParsePrefix and pass .Addr().String() when the value carries a prefix.
Example fix
// before
ip := net.ParseIP(r.ip) // r.ip == "10.0.0.1/32" -> nil
// after (normalize at the boundary)
addr, err := netip.ParseAddr(r.ip)
if err != nil {
if p, perr := netip.ParsePrefix(r.ip); perr == nil {
addr = p.Addr()
} else {
return fmt.Errorf("parse IP %s: %w", r.ip, err)
}
} Defensive patterns
Strategy: validation
Validate before calling
if net.ParseIP(candidateIP) == nil {
return fmt.Errorf("%q is not a valid IP literal; refusing to build a rule", candidateIP)
} Type guard
func isValidRuleIP(s string) bool { return net.ParseIP(s) != nil } Try / catch
if err := mgr.DeletePeerRule(rule); err != nil {
if strings.Contains(err.Error(), "parse IP") {
// corrupted rule record: drop it locally instead of retrying
removeRuleFromState(rule)
}
} Prevention
- Always derive Rule.ip from net.IP.String() at creation time.
- Never store CIDR prefixes in the ip field; normalize with netip.ParsePrefix(...).Addr().String() at boundaries.
- Validate persisted rule records with net.ParseIP before replaying deletes after a restart.
- Write tests that round-trip rule state through your persistence format to catch mangling early.
When it happens
Trigger: Hand-built Rule values in tests or restored state that carry "10.0.0.1/32", "", a hostname, or whitespace; serialization round-trips that mangle the field; rules copied from the routemanager where prefixes are the native format.
Common situations: Test fixtures written by hand; persistence layers that store prefix notation; refactors that changed the field's expected format without updating all constructors.
Related errors
- %s is not a valid input for %s. it should be formatted as "I
- invalid IP or network interface name not found
- connector type change not allowed
- service name is required
- service name exceeds maximum length of 255 characters
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/9d60a143569ab899.
Report an issue: GitHub.