AlexxIT/go2rtc · critical
%s: %w
Error message
%s: %w
What it means
The TUTK connection's worker goroutine reads packets in a loop; if the underlying transport read fails (session closed, network dropped, TUTK SDK error), the worker stores the cause wrapped as 'tutk: %w' in the connection's err field and exits. Subsequent operations on the connection surface this stored error.
Solutions
- Check c.err / returned error to get the wrapped cause (%w chain) and fix that underlying TUTK error.
- Re-dial with tutk.Dial to create a fresh session, then re-login (login/xiaofangLogin) on the new connection.
- Add reconnect-with-backoff logic around Dial and the read loop for long-lived sessions.
- Enable TUTK keepalives/heartbeats so dead sessions are detected and replaced proactively.
Example fix
// before
buf, err := conn.ReadPacket()
if err != nil { return err }
// after
buf, err := conn.ReadPacket()
if err != nil {
conn, err = reconnect(uid) // tutk.Dial + login
if err != nil { return err }
buf, err = conn.ReadPacket()
} Defensive patterns
Strategy: fallback
Validate before calling
// Detect dead session before use
if conn == nil || conn.Err() != nil {
conn, err = tutk.Dial(uid)
if err != nil { return err }
err = conn.Login()
} Type guard
func (c *Conn) Healthy() bool { return c != nil && c.err == nil } Try / catch
pkt, err := conn.ReadPacket()
if err != nil {
if errors.Is(err, conn.Err()) || errors.Is(conn.Err(), io.EOF) {
conn, err = reconnectWithBackoff(uid)
if err != nil { return err }
pkt, err = conn.ReadPacket()
}
} Prevention
- Implement automatic reconnect with exponential backoff
- Enable TUTK keepalives to detect dead sessions early
- Handle app backgrounding (mobile) by re-dialing on resume
- Log the wrapped cause for each session teardown
When it happens
Trigger: Calling Dial and then using the connection after the underlying Read fails — device disconnects mid-session, TUTK channel torn down, or the SDK returns a transmission error inside the worker loop.
Common situations: Camera reboots or loses Wi-Fi during a stream; TUTK session times out from inactivity; the device's UID becomes unreachable (relay failure); app backgrounded long enough for the peer to drop the connection.
Related errors
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/823d96f05640b349.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/tutk/conn.go:172
return c.session.SessionWrite(1, buf)
}
func (c *Conn) Error() error {
if c.err != nil {
return c.err
}
return io.EOF
}
func (c *Conn) worker() {
defer c.session.Close()
buf := make([]byte, 1200)
for {
n, err := c.Read(buf)
if err != nil {
c.err = fmt.Errorf("%s: %w", "tutk", err)
return
}
switch c.handleMsg(buf[:n]) {
case msgUnknown:
fmt.Printf("tutk: unknown msg: %x\n", buf[:n])
case msgError:
return
case msgCommandAck:
if c.cmdAck != nil {
c.cmdAck()
}
}
}
}
const (
msgUnknown = iotaView on GitHub (pinned to c245815e75)