AlexxIT/go2rtc · error
wrong topic size
Error message
wrong topic size
What it means
During PUBLISH parsing, the MQTT client reads the 2-byte topic length prefix and rejects the packet if the declared topic length exceeds the size of the remaining buffer supplied to Read. This guards against truncated or maliciously crafted packets claiming a topic longer than the actual data. It means the caller passed a buffer that does not contain the full MQTT PUBLISH payload.
Solutions
- Ensure the full MQTT packet (fixed header + remaining length + payload) is read into the buffer before calling Read; loop until the expected byte count is received.
- Check that the fixed-header byte passed as b0 corresponds to the same buffer b you pass in; trim exactly the header bytes before Read.
- Verify the peer/broker is speaking MQTT 3.1.x as expected and the stream is not corrupted or out of sync.
- Log the declared topic length vs buffer size at the call site to identify short reads.
Example fix
// before buf := make([]byte, 64) n, _ := conn.Read(buf) topic, payload, err := client.Read(buf[0], buf[:n]) // after // read the full packet per remaining-length before parsing header := make([]byte, 2) io.ReadFull(conn, header) remLen := int(header[1]) body := make([]byte, remLen) io.ReadFull(conn, body) topic, payload, err := client.Read(header[0], body)
Defensive patterns
Strategy: validation
Validate before calling
// before calling Read, ensure the buffer holds the declared payload
if len(b) < 2 { return errors.New("buffer too short for topic length") }
declared := int(binary.BigEndian.Uint16(b))
if declared > len(b)-2 { return errors.New("truncated MQTT publish packet") } Try / catch
if topic, payload, err := c.Read(b0, b); err != nil {
if strings.Contains(err.Error(), "wrong topic size") {
// resync stream: drop this packet and re-read a full frame
return resyncStream(conn)
}
return err
} Prevention
- Always read the complete MQTT frame (header + remaining length) before parsing.
- Use io.ReadFull instead of a single conn.Read to avoid short reads.
- Validate declared lengths against buffer sizes at protocol boundaries.
- Log declared-vs-actual sizes when parsing fails to detect stream desync early.
When it happens
Trigger: Calling client.Read (pkg/mqtt/client.go Read) with a byte slice whose declared topic length (first 2 bytes after the fixed header) is greater than len(b)-2, e.g. a truncated PUBLISH packet or reading fewer bytes than the packet length.
Common situations: Reading from the socket with a short read / partial frame; feeding a packet captured before the full body arrived; a corrupted or non-MQTT stream being parsed as MQTT; off-by-one when trimming the fixed header before calling Read.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/e5059c0a0a97a776.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/mqtt/client.go:94
size, err := ReadLen(c.conn)
if err != nil {
return "", nil, err
}
b0 := b[0]
b = make([]byte, size)
if _, err = io.ReadFull(c.conn, b); err != nil {
return "", nil, err
}
if b0&0xF0 != PUBLISH {
return "", nil, nil
}
i := binary.BigEndian.Uint16(b)
if uint32(i) > size {
return "", nil, errors.New("wrong topic size")
}
b = b[2:]
if qos := (b0 >> 1) & 0b11; qos == 0 {
return string(b[:i]), b[i:], nil
}
// response with packet ID
_, _ = c.conn.Write([]byte{PUBACK, 2, b[i], b[i+1]})
return string(b[2:i]), b[i+2:], nil
}
func (c *Client) Close() error {
// TODO: Teardown
return c.conn.Close()
}View on GitHub (pinned to c245815e75)