netbirdio/netbird · error

build packet: %w

Error message

build packet: %w

What it means

TracePacketFromBuilder wraps any failure from PacketBuilder.Build, which itself aggregates the earlier builder errors: mixed address families in buildIPLayer, checksum network-layer mismatch in the TCP/UDP builders, or SerializeLayers failures (length overflow, unencodable layer state). This is the single error surface a caller of the trace API sees, so the underlying cause is only visible in the wrapped chain.

Source

Thrown at client/firewall/uspfilter/tracer.go:264

	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
	}
}

func (m *Manager) TracePacketFromBuilder(builder *PacketBuilder) (*PacketTrace, error) {
	packetData, err := builder.Build()
	if err != nil {
		return nil, fmt.Errorf("build packet: %w", err)
	}

	return m.TracePacket(packetData, builder.Direction), nil
}

func (m *Manager) TracePacket(packetData []byte, direction fw.RuleDirection) *PacketTrace {

	d := m.decoders.Get().(*decoder)
	defer m.decoders.Put(d)

	trace := &PacketTrace{Direction: direction}

	// Initial packet decoding
	if err := d.decodePacket(packetData); err != nil {
		trace.AddResult(StageReceived, fmt.Sprintf("Failed to decode packet: %v", err), false)
		return trace
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Inspect errors.Is/errors.Unwrap on the returned error to find which builder stage failed (family, checksum, or serialize)
  2. Validate before calling: both IPs valid and same-family, ports set, PayloadSize bounded
  3. Normalize addresses with Unmap() at ingestion so v4-mapped v6 does not sneak into the family check
  4. Add per-protocol happy-path tests so regressions surface as specific builder errors, not this generic wrap

Example fix

// before
trace, err := m.TracePacketFromBuilder(b)

// after
if err := b.Validate(); err != nil { // same-family + bounds checks
    return nil, err
}
trace, err := m.TracePacketFromBuilder(b)
if err != nil {
    return nil, fmt.Errorf("trace packet: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if !p.SrcIP.IsValid() || !p.DstIP.IsValid() || p.SrcIP.Is4() != p.DstIP.Is4() {
    return errors.New("packet builder inputs invalid")
}
if p.PayloadSize > 0xffff-28 {
    return errors.New("payload too large")
}
trace, err := m.TracePacketFromBuilder(p)

Type guard

func traceInputsValid(p *PacketBuilder) bool {
    return p.SrcIP.IsValid() && p.DstIP.IsValid() &&
        p.SrcIP.Is4() == p.DstIP.Is4() && sanePayloadSize(p.PayloadSize)
}

Try / catch

trace, err := m.TracePacketFromBuilder(b)
if err != nil {
    // unwrap to classify: family / checksum / serialize, then fix inputs and rebuild
    log.Debugf("trace build failed: %v", err)
    return nil, err
}

Prevention

When it happens

Trigger: Calling TracePacketFromBuilder with a PacketBuilder whose SrcIP/DstIP families differ or one is unset; PayloadSize beyond the 16-bit length limits; an ICMPv6 trace where the network-layer assertion failed; any refactor that breaks the ipLayer/transportLayer pairing.

Common situations: Building trace/diagnostics tooling on uspfilter and constructing PacketBuilder values from user or config input without prior normalization; automated tests sweeping address families and payload sizes and hitting an unhandled combination.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/83f29fc1ee7e382f. Report an issue: GitHub.