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

  1. 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.
  2. 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.
  3. Verify the peer/broker is speaking MQTT 3.1.x as expected and the stream is not corrupted or out of sync.
  4. 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

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)