AlexxIT/go2rtc · error
wrong start byte
Error message
wrong start byte
What it means
bubble.Client.Read (pkg/bubble/client.go:184) parses the wire framing of the Bubble protocol: it reads a 10-byte header via io.ReadFull and requires the first byte to be SyncByte. If the stream is misaligned or carries foreign data, b[0] != SyncByte and the client returns "wrong start byte". This is a stream-synchronization failure: the reader is no longer at a packet boundary.
Solutions
- Treat the connection as unrecoverable and reconnect: re-Dial and re-authenticate; the stream framing cannot be trusted.
- Check the network path (no TCP proxies/inspectors rewriting the stream).
- Verify the camera firmware hasn't changed the framing; update the driver if the protocol changed.
- Ensure no code path reads from c.r outside Read (extraneous reads desync the framing).
- Capture traffic on the port and confirm the packets start with the expected sync byte.
Example fix
// before: retrying Read on a desynced stream fails forever
for {
cmd, b, err := client.Read() // keeps returning wrong start byte
}
// after: resync by reconnecting
if _, _, err := client.Read(); err != nil {
client.Close()
client, err = bubble.Dial(ctx, log, addr, user, pass)
} Defensive patterns
Strategy: fallback
Try / catch
cmd, b, err := client.Read()
if err != nil && err.Error() == "wrong start byte" {
client.Close()
client, err = reconnectWithBackoff(ctx) // framing lost; full reconnect
} Prevention
- Never read from the connection outside the library's Read
- Reconnect on any framing error instead of retrying reads
- Keep the TCP path free of rewriting middleboxes
- Watch for camera resets and re-Dial proactively
When it happens
Trigger: Calling Read (directly or via Dial, Handle, ReadResponseHeader) when the first byte of the next 10-byte header is not SyncByte — misaligned stream, partial/garbage data from the camera, or reading from the wrong offset.
Common situations: Camera dropped/corrupted TCP stream mid-session and bytes shifted; a proxy or middlebox altering the payload; reading a non-Bubble stream from that port; a bug leaving a partial packet read so the next Read starts mid-payload.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/5206671587e83a1c.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/bubble/client.go:184
copy(b[10:], payload)
_, err := c.conn.Write(b)
return err
}
func (c *Client) Read() (byte, []byte, error) {
if err := c.conn.SetReadDeadline(time.Now().Add(Timeout)); err != nil {
return 0, nil, err
}
// 0xAA + size uint32 + cmd byte + ts uint32 + payload
b := make([]byte, 10)
if _, err := io.ReadFull(c.r, b); err != nil {
return 0, nil, err
}
if b[0] != SyncByte {
return 0, nil, errors.New("wrong start byte")
}
size := binary.BigEndian.Uint32(b[1:])
payload := make([]byte, size-1-4)
if _, err := io.ReadFull(c.r, payload); err != nil {
return 0, nil, err
}
//timestamp := binary.BigEndian.Uint32(b[6:]) // in ms
return b[5], payload, nil
}
func (c *Client) Play() error {
// yeah, there's no mistake about the little endian
b := make([]byte, 16)
binary.LittleEndian.PutUint32(b, uint32(c.channel))
binary.LittleEndian.PutUint32(b[4:], uint32(c.stream))View on GitHub (pinned to c245815e75)