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

  1. Check c.err / returned error to get the wrapped cause (%w chain) and fix that underlying TUTK error.
  2. Re-dial with tutk.Dial to create a fresh session, then re-login (login/xiaofangLogin) on the new connection.
  3. Add reconnect-with-backoff logic around Dial and the read loop for long-lived sessions.
  4. 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

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 = iota

View on GitHub (pinned to c245815e75)