{"record":{"id":"1a9d482bb3bf2196","repo":"AlexxIT/go2rtc","slug":"ring-disconnect","errorCode":null,"errorMessage":"ring: disconnect","messagePattern":"ring: disconnect","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/ring/client.go","lineNumber":128,"sourceCode":"\tprod.FormatName = \"ring/webrtc\"\n\tprod.Mode = core.ModeActiveProducer\n\tprod.Protocol = \"ws\"\n\tprod.URL = rawURL\n\n\tclient.wsClient.onMessage = func(msg WSMessage) {\n\t\tclient.onWSMessage(msg)\n\t}\n\n\tclient.wsClient.onError = func(err error) {\n\t\t// fmt.Printf(\"ring: error: %s\\n\", err.Error())\n\t\tclient.Stop()\n\t\tclient.connected.Done(err)\n\t}\n\n\tclient.wsClient.onClose = func() {\n\t\t// fmt.Println(\"ring: disconnect\")\n\t\tclient.Stop()\n\t\tclient.connected.Done(errors.New(\"ring: disconnect\"))\n\t}\n\n\tprod.Listen(func(msg any) {\n\t\tswitch msg := msg.(type) {\n\t\tcase *pion.ICECandidate:\n\t\t\t_ = sendOffer.Wait()\n\n\t\t\ticeCandidate := msg.ToJSON()\n\n\t\t\t// skip empty ICE candidates\n\t\t\tif iceCandidate.Candidate == \"\" {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ticePayload := map[string]interface{}{\n\t\t\t\t\"ice\":        iceCandidate.Candidate,\n\t\t\t\t\"mlineindex\": iceCandidate.SDPMLineIndex,\n\t\t\t}","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/ring/client.go#L110-L146","documentation":"The ring client signals connection completion through a wait group; when the underlying WebSocket closes, onClose stops the client and completes it with this \"ring: disconnect\" error, which surfaces to the Dial/caller as the connection failure reason.","triggerScenarios":"The WebSocket connection to Ring's servers drops during or after setup — network interruption, Ring closing the socket (server-side rejection, auth expiry), or the client being stopped while a Dial is still waiting.","commonSituations":"Expired or revoked Ring refresh token causing the server to close the socket; flaky network/NAT timeouts mid-session; Ring service incident.","solutions":["Refresh the Ring refresh_token and dial again","Implement reconnect logic with backoff around Dial/usage","Check network stability / firewall blocking the WebSocket","Check Ring service status if it disconnects immediately every time"],"exampleFix":"// before\nclient, err := Dial(ctx, query)\n// after\nclient, err := Dial(ctx, query)\nif err != nil && err.Error() == \"ring: disconnect\" {\n    time.Sleep(retryBackoff)\n    client, err = Dial(ctx, query) // with refreshed token\n}","handlingStrategy":"retry","validationCode":"if token == \"\" || time.Since(tokenIssuedAt) > tokenMaxAge {\n    token = refreshRingToken() // avoid predictable disconnects from auth expiry\n}","typeGuard":null,"tryCatchPattern":"client, err := Dial(ctx, query)\nif err != nil && err.Error() == \"ring: disconnect\" {\n    if client, err = reconnectWithBackoff(ctx, query); err != nil {\n        return err\n    }\n}","preventionTips":["Refresh the Ring refresh token proactively before it expires","Wrap ring sessions in an auto-reconnect loop with exponential backoff","Monitor WebSocket stability; alert on frequent disconnects"],"tags":["websocket","disconnect","network"],"backgroundTag":"websocket-disconnected","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"}