{"record":{"id":"823d96f05640b349","repo":"AlexxIT/go2rtc","slug":"s-w","errorCode":null,"errorMessage":"%s: %w","messagePattern":"%s: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/tutk/conn.go","lineNumber":172,"sourceCode":"\treturn c.session.SessionWrite(1, buf)\n}\n\nfunc (c *Conn) Error() error {\n\tif c.err != nil {\n\t\treturn c.err\n\t}\n\treturn io.EOF\n}\n\nfunc (c *Conn) worker() {\n\tdefer c.session.Close()\n\n\tbuf := make([]byte, 1200)\n\n\tfor {\n\t\tn, err := c.Read(buf)\n\t\tif err != nil {\n\t\t\tc.err = fmt.Errorf(\"%s: %w\", \"tutk\", err)\n\t\t\treturn\n\t\t}\n\n\t\tswitch c.handleMsg(buf[:n]) {\n\t\tcase msgUnknown:\n\t\t\tfmt.Printf(\"tutk: unknown msg: %x\\n\", buf[:n])\n\t\tcase msgError:\n\t\t\treturn\n\t\tcase msgCommandAck:\n\t\t\tif c.cmdAck != nil {\n\t\t\t\tc.cmdAck()\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst (\n\tmsgUnknown = iota","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/tutk/conn.go#L154-L190","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbuf, err := conn.ReadPacket()\nif err != nil { return err }\n// after\nbuf, err := conn.ReadPacket()\nif err != nil {\n\tconn, err = reconnect(uid) // tutk.Dial + login\n\tif err != nil { return err }\n\tbuf, err = conn.ReadPacket()\n}","handlingStrategy":"fallback","validationCode":"// Detect dead session before use\nif conn == nil || conn.Err() != nil {\n\tconn, err = tutk.Dial(uid)\n\tif err != nil { return err }\n\terr = conn.Login()\n}","typeGuard":"func (c *Conn) Healthy() bool { return c != nil && c.err == nil }","tryCatchPattern":"pkt, err := conn.ReadPacket()\nif err != nil {\n\tif errors.Is(err, conn.Err()) || errors.Is(conn.Err(), io.EOF) {\n\t\tconn, err = reconnectWithBackoff(uid)\n\t\tif err != nil { return err }\n\t\tpkt, err = conn.ReadPacket()\n\t}\n}","preventionTips":["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"],"tags":["tutk","network","connection","reconnect"],"backgroundTag":"network-request-failed","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}