slackhq/nebula · error
invalid mask: %s for cidr: %s
Error message
invalid mask: %s for cidr: %s
What it means
newCalculatedRemote builds a calculated_remote from a lighthouse CIDR and a mask CIDR; the mask must address the same IP family (same bit length) as the CIDR so masking can combine them. It throws this when the mask's address family (e.g. IPv4) differs from the CIDR's (e.g. IPv6), making the mask unusable for that CIDR.
Source
Thrown at calculated_remote.go:26
"net/netip"
"strconv"
"github.com/gaissmai/bart"
"github.com/slackhq/nebula/config"
)
// This allows us to "guess" what the remote might be for a host while we wait
// for the lighthouse response. See "lighthouse.calculated_remotes" in the
// example config file.
type calculatedRemote struct {
ipNet netip.Prefix
mask netip.Prefix
port uint32
}
func newCalculatedRemote(cidr, maskCidr netip.Prefix, port int) (*calculatedRemote, error) {
if maskCidr.Addr().BitLen() != cidr.Addr().BitLen() {
return nil, fmt.Errorf("invalid mask: %s for cidr: %s", maskCidr, cidr)
}
masked := maskCidr.Masked()
if port < 0 || port > math.MaxUint16 {
return nil, fmt.Errorf("invalid port: %d", port)
}
return &calculatedRemote{
ipNet: maskCidr,
mask: masked,
port: uint32(port),
}, nil
}
func (c *calculatedRemote) String() string {
return fmt.Sprintf("CalculatedRemote(mask=%v port=%d)", c.ipNet, c.port)
}
View on GitHub (pinned to dd8f660c0a)
Solutions
- Make the mask prefix use the same IP family as the CIDR (e.g. mask=255.255.0.0/16 for an IPv4 CIDR)
- Check the error text: the first prefix is the mask, the second is the CIDR; fix the one with the wrong family
- Validate family equality before calling newCalculatedRemote by comparing maskCidr.Addr().BitLen() == cidr.Addr().BitLen()
Example fix
// before
lighthouse:
calculated_remotes:
10.0.42.0/24:
- mask: fd00::/8
port: 4242
// after
lighthouse:
calculated_remotes:
10.0.42.0/24:
- mask: 10.0.0.0/8
port: 4242 Defensive patterns
Strategy: validation
Validate before calling
func validFamily(cidr, mask netip.Prefix) bool {
return cidr.Addr().BitLen() == mask.Addr().BitLen()
}
// call before newCalculatedRemote; reject config entries where !validFamily Type guard
func isMaskValidForCidr(mask, cidr netip.Prefix) bool {
return mask.IsValid() && cidr.IsValid() && mask.Addr().BitLen() == cidr.Addr().BitLen()
} Try / catch
cr, err := newCalculatedRemote(cidr, maskCidr, port)
if err != nil {
var famErr bool
if strings.HasPrefix(err.Error(), "invalid mask:") {
famErr = true // log config key and skip or fail reload
}
return fmt.Errorf("calculated_remotes %s: %w", cidr, err)
} Prevention
- Keep IPv4 CIDRs paired with IPv4 masks and IPv6 with IPv6 in calculated_remotes
- Compare Addr().BitLen() of mask and cidr before submitting config
- When migrating overlay networks between families, update both cidr keys and masks together
When it happens
Trigger: Calling newCalculatedRemote (via lighthouse.calculated_remotes config loading) where the `mask` prefix and the entry's CIDR key use different families — e.g. cidr=10.0.42.0/24 with mask=fd00::/8, or vice versa.
Common situations: Nebula calculated_remotes configs where a mask was copy-pasted from an IPv6 block onto an IPv4 CIDR (or the opposite), often when migrating an overlay network between address families.
Related errors
- invalid port: %d
- config `%s` has invalid type: %T
- config `%s` has invalid CIDR: %s
- config '%s.%s': %w
- calculated_remotes entry has invalid type: %T
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/7a8b92bf8a6861c1.
Report an issue: GitHub.