netbirdio/netbird · error
mixed address families: src=%s dst=%s
Error message
mixed address families: src=%s dst=%s
What it means
The tracer's PacketBuilder refuses to synthesize an IP header when source and destination addresses belong to different address families (one Is4, the other not). gopacket cannot build a coherent IPv4 or IPv6 layer from mixed inputs, and the checksum/protocol selection logic (getIPProtocolNumber with Is6) would be ambiguous. Notably, an unset netip.Addr is neither Is4 nor Is6, so leaving DstIP (or SrcIP) at its zero value while setting the other to a real address also trips this check.
Source
Thrown at client/firewall/uspfilter/tracer.go:139
pktLayers := []gopacket.SerializableLayer{ipLayer}
transportLayer, err := p.buildTransportLayer(ipLayer)
if err != nil {
return nil, err
}
pktLayers = append(pktLayers, transportLayer...)
if p.PayloadSize > 0 {
payload := make([]byte, p.PayloadSize)
pktLayers = append(pktLayers, gopacket.Payload(payload))
}
return serializePacket(pktLayers)
}
func (p *PacketBuilder) buildIPLayer() (gopacket.SerializableLayer, error) {
if p.SrcIP.Is4() != p.DstIP.Is4() {
return nil, fmt.Errorf("mixed address families: src=%s dst=%s", p.SrcIP, p.DstIP)
}
proto := getIPProtocolNumber(p.Protocol, p.SrcIP.Is6())
if p.SrcIP.Is6() {
return &layers.IPv6{
Version: 6,
HopLimit: 64,
NextHeader: proto,
SrcIP: p.SrcIP.AsSlice(),
DstIP: p.DstIP.AsSlice(),
}, nil
}
return &layers.IPv4{
Version: 4,
TTL: 64,
Protocol: proto,
SrcIP: p.SrcIP.AsSlice(),
DstIP: p.DstIP.AsSlice(),
}, nilView on GitHub (pinned to 93e97f4bf1)
Solutions
- Validate both addresses before calling Build: ensure SrcIP.IsValid(), DstIP.IsValid(), and SrcIP.Is4() == DstIP.Is4()
- Normalize inputs with Unmap() at the boundary so a v4-mapped v6 address becomes plain v4 and families line up
- Default the unset side explicitly (e.g. copy the interface family) instead of leaving the zero value
- Fail early at the API edge of your tooling with a clear message about which side is missing
Example fix
// before
b := &tracer.PacketBuilder{SrcIP: localV4, DstIP: peerAddr, ...}
data, err := b.Build()
// after
if !b.SrcIP.IsValid() || !b.DstIP.IsValid() || b.SrcIP.Is4() != b.DstIP.Is4() {
return fmt.Errorf("trace endpoints must be valid and same-family: src=%s dst=%s", b.SrcIP, b.DstIP)
}
peerAddr = peerAddr.Unmap()
data, err := b.Build() Defensive patterns
Strategy: validation
Validate before calling
func sameFamily(a, b netip.Addr) bool {
return a.IsValid() && b.IsValid() && a.Is4() == b.Is4()
}
if !sameFamily(b.SrcIP, b.DstIP) {
return fmt.Errorf("trace needs same-family endpoints: %s -> %s", b.SrcIP, b.DstIP)
}
trace, err := m.TracePacketFromBuilder(b) Type guard
func validTraceAddrs(src, dst netip.Addr) bool {
return src.IsValid() && dst.IsValid() && src.Is4() == dst.Is4()
} Try / catch
if _, err := m.TracePacketFromBuilder(b); err != nil {
if strings.Contains(err.Error(), "mixed address families") {
// fix address selection (e.g. pick the v6 peer address for a v6 trace)
}
} Prevention
- Unmap() every parsed address at the boundary so v4-mapped v6 never reaches the builder
- Reject zero-value addresses early: unset netip.Addr is neither v4 nor v6
- Default the missing endpoint from the interface address family instead of leaving it unset
When it happens
Trigger: Building a trace packet with an IPv4 SrcIP and an IPv6 DstIP or vice versa; omitting one of the two addresses so it stays as the invalid zero-value netip.Addr; copying addresses from a config map where one side was parsed with a different family than the other.
Common situations: Writing new trace/troubleshooting code on top of the uspfilter tracer and forgetting to set both endpoints; mixing a peer's v6 overlay address with the local v4 interface address in diagnostics tooling; parsing one address with netip.MustParseAddr and the other from a legacy net.IP slice of 16-byte v4-mapped form that was never Unmap()-ed.
Related errors
- set network layer for TCP checksum: %w
- set network layer for UDP checksum: %w
- serialize packet: %w
- build packet: %w
- connector type change not allowed
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/3cab59bc078f8f7f.
Report an issue: GitHub.