slackhq/nebula · warning

ErrIPv6CouldNotFindPayload

ErrIPv6CouldNotFindPayload

Error message

could not find payload in ipv6 packet

What it means

ErrIPv6CouldNotFindPayload is returned by IPv6FindUpperProtocol when the packet is too short to hold an IPv6 header or the extension header chain (hop-by-hop, routing, destination, fragment) is truncated before a terminal upper-layer protocol is reached, within the 8-extension-header limit.

Source

Thrown at iputil/packet.go:13

package iputil

import (
	"encoding/binary"
	"errors"

	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

// ErrIPv6CouldNotFindPayload is returned when the ipv6 extension header chain is truncated before a terminal
// upper layer protocol is reached.
var ErrIPv6CouldNotFindPayload = errors.New("could not find payload in ipv6 packet")

const (
	// MaxIPv4RejectPacketSize is the largest IPv4 reject packet:
	// - 20 byte ipv4 header
	// - 8 byte icmpv4 header
	// - 68 byte body (60 byte max orig ipv4 header + 8 byte orig icmpv4 header)
	maxIPv4RejectPacketSize = ipv4.HeaderLen + 8 + 60 + 8

	// MaxRejectPacketSize is sized for the largest possible reject packet (IPv6):
	// - 40 byte ipv6 header
	// - 8 byte icmpv6 header
	// - up to 1000 byte body (original packet, possibly truncated. We want to stay
	//   under the MTU with Nebula overhead included)
	maxIPv6RejectPacketSize = ipv6.HeaderLen + 8 + 1000

	MaxRejectPacketSize = maxIPv6RejectPacketSize

	IPProtocolICMP        = 1

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Check NIC offload/MTU settings and capture path for truncation
  2. Validate the packet length before parsing; treat the packet as malformed and drop it
  3. If this is your code calling IPv6FindUpperProtocol, guard on len(packet) >= ipv6.HeaderLen first
  4. Use errors.Is(err, ErrIPv6CouldNotFindPayload) to distinguish malformed packets from real protocol data

Example fix

// before
nh, off, frag, anyFrag, err := IPv6FindUpperProtocol(packet)
handle(nh, off)
// after
nh, off, frag, anyFrag, err := IPv6FindUpperProtocol(packet)
if errors.Is(err, ErrIPv6CouldNotFindPayload) {
    // malformed/truncated ipv6 packet: drop
    return
}
Defensive patterns

Strategy: try-catch

Validate before calling

if len(packet) < 40 { // ipv6.HeaderLen
    // too short to be an ipv6 packet; skip parse
}

Type guard

func isParseableIPv6(packet []byte) bool { return len(packet) >= 40 }

Try / catch

nh, off, frag, anyFrag, err := IPv6FindUpperProtocol(packet)
if errors.Is(err, ErrIPv6CouldNotFindPayload) {
    // truncated/malformed extension chain: drop or log counter
    return
}

Prevention

When it happens

Trigger: Parsing a packet shorter than ipv6.HeaderLen; an extension header claims more bytes than remain (len(packet) < offset+2); a fragment header encountered without a following payload; more than maxIPv6ExtHeaders extension headers.

Common situations: Corrupt or truncated packets from a bad NIC/MTU mismatch; malicious malformed packets hitting the firewall path; reading a partial buffer from the UDP socket.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/7d4b478194615f50. Report an issue: GitHub.