netbirdio/netbird · error
serialize packet: %w
Error message
serialize packet: %w
What it means
SerializeLayers with ComputeChecksums and FixLengths fails when the layer stack cannot be encoded into the serialize buffer: payload sizes that overflow the 16-bit length fields (UDP length or IPv4 total length beyond 65535), an uninitialized required layer (e.g. ICMPv6 without a network layer available for its checksum), or inconsistent layer state that FixLengths cannot repair. The tracer hits it while turning the assembled gopacket layers into raw bytes for injection into the trace path.
Source
Thrown at client/firewall/uspfilter/tracer.go:240
}
icmp := &layers.ICMPv4{
TypeCode: layers.CreateICMPv4TypeCode(p.ICMPType, p.ICMPCode),
}
if p.ICMPType == layers.ICMPv4TypeEchoRequest || p.ICMPType == layers.ICMPv4TypeEchoReply {
icmp.Id = uint16(1)
icmp.Seq = uint16(1)
}
return []gopacket.SerializableLayer{icmp}, nil
}
func serializePacket(layers []gopacket.SerializableLayer) ([]byte, error) {
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
if err := gopacket.SerializeLayers(buf, opts, layers...); err != nil {
return nil, fmt.Errorf("serialize packet: %w", err)
}
return buf.Bytes(), nil
}
func getIPProtocolNumber(protocol fw.Protocol, isV6 bool) layers.IPProtocol {
switch protocol {
case fw.ProtocolTCP:
return layers.IPProtocolTCP
case fw.ProtocolUDP:
return layers.IPProtocolUDP
case fw.ProtocolICMP:
if isV6 {
return layers.IPProtocolICMPv6
}
return layers.IPProtocolICMPv4
default:
return 0
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Cap PayloadSize so that IP header + transport header + payload stays under 65535 (and under the path MTU for realistic traces)
- Ensure the ICMPv6 path always receives a real gopacket.NetworkLayer so its checksum prerequisite is satisfied before serialization
- Read the wrapped gopacket error text; it names the exact layer and constraint that failed
- Add a regression test per protocol (TCP/UDP/ICMP/v4/v6) covering minimum and maximum sane sizes
Example fix
// before
p.PayloadSize = 70000
// after
const maxTracePayload = 0xffff - 28 // IPv4 + UDP header worst case
if p.PayloadSize > maxTracePayload {
p.PayloadSize = maxTracePayload
} Defensive patterns
Strategy: validation
Validate before calling
const maxPayload = 0xffff - 28 // worst-case IPv4+UDP headers
if p.PayloadSize > maxPayload {
p.PayloadSize = maxPayload
}
data, err := m.TracePacketFromBuilder(p) Type guard
func sanePayloadSize(size int) bool {
return size >= 0 && size <= 0xffff-28
} Try / catch
if _, err := serializePacket(pktLayers); err != nil {
if strings.Contains(err.Error(), "serialize packet") {
// drop optional layers (payload) and retry once with a smaller trace packet
}
return err
} Prevention
- Bound PayloadSize by the 16-bit length fields, not just the MTU
- Never ignore the ipLayer-to-NetworkLayer assertion result for ICMPv6 before serializing
- Golden-file tests per protocol and family catch serialization regressions cheaply
When it happens
Trigger: Setting PacketBuilder.PayloadSize large enough that IPv4 total length or the UDP length field overflows 16 bits; serializing ICMPv6 when the ipLayer type assertion to gopacket.NetworkLayer failed (its SetNetworkLayerForChecksum error is deliberately ignored in buildICMPLayer); building a layer combination the library cannot encode.
Common situations: Stress-testing the tracer with jumbo or synthetic payload sizes; tracing ICMPv6 after refactoring the IP layer construction; gopacket version changes introducing stricter length validation.
Related errors
- mixed address families: src=%s dst=%s
- set network layer for TCP checksum: %w
- set network layer for UDP checksum: %w
- build packet: %w
- serialize layers: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/a64ecb14d88eb1fa.
Report an issue: GitHub.