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
- Read exactly the announced frame length (io.ReadFull) before unmarshalling
- Check len(buf) against the expected header size before calling Unmarshal*
- 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
- Use io.ReadFull for fixed headers and announced body lengths
- Never treat a single Read's return as a complete frame
- Check lengths before unmarshalling in tests and fixtures too
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
- invalid magic header
- no keys found in bundle
- failed to decode PEM data
- invalid rule format: expected protocol/port or protocol/port
- icmp does not accept ports; use 'icmp' without '/…'
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/f35f0b07141c8584.
Report an issue: GitHub.