netbirdio/netbird · error
serialize layers: %w
Error message
serialize layers: %w
What it means
Returned by SrcFaker.SendPkg when gopacket.SerializeLayers fails to serialize the IP header + UDP header + payload into the reuse buffer with ComputeChecksums and FixLengths enabled. gopacket errors here when a layer is internally inconsistent - most commonly the UDP length field overflowing 16 bits (payload too large for one UDP datagram) or the fixed-size IPv4 header receiving invalid values. The prebuilt headers come from NewSrcFaker's prepareHeaders, so in practice the variable is the payload size.
Source
Thrown at client/iface/wgproxy/udp/rawsocket.go:92
return f, nil
}
func (f *SrcFaker) Close() error {
return f.rawSocket.Close()
}
func (f *SrcFaker) SendPkg(data []byte) (int, error) {
defer func() {
if err := f.layerBuffer.Clear(); err != nil {
log.Errorf("failed to clear layer buffer: %s", err)
}
}()
payload := gopacket.Payload(data)
err := gopacket.SerializeLayers(f.layerBuffer, serializeOpts, f.ipH, f.udpH, payload)
if err != nil {
return 0, fmt.Errorf("serialize layers: %w", err)
}
n, err := f.rawSocket.WriteTo(f.layerBuffer.Bytes(), f.localHostAddr)
if err != nil {
return 0, fmt.Errorf("write to raw conn: %w", err)
}
return n, nil
}
func prepareHeaders(dstPort int, srcAddr *net.UDPAddr) (gopacket.SerializableLayer, gopacket.SerializableLayer, error) {
var ipH gopacket.SerializableLayer
var networkLayer gopacket.NetworkLayer
// Check if source IP is IPv4 or IPv6
if srcAddr.IP.To4() != nil {
// IPv4
ipv4 := &layers.IPv4{
DstIP: localHostNetIPAddrV4.IP,
SrcIP: srcAddr.IP,View on GitHub (pinned to 93e97f4bf1)
Solutions
- Check the interface MTU: keep it in the normal 1280-1500 (or at most ~9000 jumbo) range - `ip link show wt0`
- Inspect the wrapped gopacket error to see which layer rejected the write
- Restart the agent after fixing MTU so proxies rebuild with a sane buffer size (mtu+bufsize.WGBufferOverhead)
Defensive patterns
Strategy: validation
Validate before calling
// cap payloads to what one UDP datagram can carry before serializing
const maxUDPPayload = 65535 - 8 - 20 // v4 worst case
func fitForDatagram(data []byte) ([]byte, bool) {
if len(data) <= maxUDPPayload {
return data, true
}
return nil, false
} Try / catch
if _, err := f.SendPkg(data); err != nil {
if strings.Contains(err.Error(), "serialize layers") {
log.Errorf("packet of %d bytes exceeds UDP datagram limits; check interface MTU", len(data))
return // drop - cannot be sent as one datagram
}
return err
} Prevention
- Keep the NetBird interface MTU within 1280-9000; never set it near 65535
- Disable GRO/GSO aggregation feeding the proxy if super-packets appear
- Buffer sizes derive from mtu+bufsize.WGBufferOverhead - re-create proxies after MTU changes
When it happens
Trigger: A proxied packet larger than what a UDP datagram can express (data > ~65507 bytes after headers), which requires an oversized interface MTU (e.g. jumbo/UDP GRO aggregation bugs feeding SendPkg buffers beyond 64KB); or corrupted header state after the buffer was not cleared (the deferred layerBuffer.Clear guards that).
Common situations: MTU misconfiguration on the NetBird interface (values near or above 65535); virtualization/GRO delivering aggregated super-packets; memory corruption. Rare in the field.
Related errors
- serialize packet: %w
- mixed address families: src=%s dst=%s
- set network layer for TCP checksum: %w
- set network layer for UDP checksum: %w
- build packet: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/4a357f4acc4010fd.
Report an issue: GitHub.