netbirdio/netbird · error

invalid message length

Error message

invalid message length

What it means

ErrInvalidMessageLength is the messages package's frame-length guard: every Marshal/Unmarshal helper first checks that the buffer is at least the fixed header size for its message type (protocol header, magic byte, and peer-ID fields as applicable). It fires when fewer bytes than a minimal valid frame are supplied, before any content is interpreted.

Source

Thrown at shared/relay/messages/message.go:53

	sizeOfVersionByte = 1
	sizeOfMsgType     = 1
	sizeOfProtoHeader = sizeOfVersionByte + sizeOfMsgType

	// auth message
	sizeOfMagicByte     = 4
	headerSizeAuth      = sizeOfMagicByte + peerIDSize
	offsetMagicByte     = sizeOfProtoHeader
	offsetAuthPeerID    = sizeOfProtoHeader + sizeOfMagicByte
	headerTotalSizeAuth = sizeOfProtoHeader + headerSizeAuth

	// transport
	headerSizeTransport      = peerIDSize
	offsetTransportID        = sizeOfProtoHeader
	headerTotalSizeTransport = sizeOfProtoHeader + headerSizeTransport
)

var (
	ErrInvalidMessageLength = errors.New("invalid message length")
	ErrUnsupportedVersion   = errors.New("unsupported version")

	magicHeader = []byte{0x21, 0x12, 0xA4, 0x42}

	healthCheckMsg = []byte{byte(CurrentProtocolVersion), byte(MsgTypeHealthCheck)}
)

type MsgType byte

func (m MsgType) String() string {
	switch m {
	case MsgTypeHello:
		return "hello"
	case MsgTypeHelloResponse:
		return "hello response"
	case MsgTypeAuth:
		return "auth"
	case MsgTypeAuthResponse:

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Read exactly the announced frame length (io.ReadFull) before unmarshalling
  2. Check len(buf) against the expected header size before calling Unmarshal*
  3. Log the received length to spot truncation introduced upstream

Example fix

// before
n, _ := conn.Read(buf)
msg, err := messages.UnmarshalAuthMsg(buf)

// after
if _, err := io.ReadFull(conn, buf[:headerTotalSizeAuth]); err != nil {
	return err
}
msg, err := messages.UnmarshalAuthMsg(buf[:headerTotalSizeAuth])
Defensive patterns

Strategy: validation

Validate before calling

if len(buf) < messages.HeaderTotalSizeAuth { // or the header size for the expected message type
	return fmt.Errorf("frame too short: got %d bytes", len(buf))
}
peerID, payload, err := messages.UnmarshalAuthMsg(buf)

Try / catch

if _, _, err := messages.UnmarshalAuthMsg(buf); err != nil {
	if errors.Is(err, messages.ErrInvalidMessageLength) {
		// incomplete read: read the full frame length and retry the unmarshal
	}
	return err
}

Prevention

When it happens

Trigger: A short read from a stream (not using io.ReadFull), a datagram truncated below the header size, or calling an Unmarshal* helper on an empty or undersized buffer.

Common situations: Custom integrations reading relay sockets with buffers smaller than the header; partial reads from net.Conn.Read treated as complete frames; truncated test fixtures.

Related errors


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