AlexxIT/go2rtc · error
ring: disconnect
Error message
ring: disconnect
What it means
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.
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
Example fix
// before
client, err := Dial(ctx, query)
// after
client, err := Dial(ctx, query)
if err != nil && err.Error() == "ring: disconnect" {
time.Sleep(retryBackoff)
client, err = Dial(ctx, query) // with refreshed token
} Defensive patterns
Strategy: retry
Validate before calling
if token == "" || time.Since(tokenIssuedAt) > tokenMaxAge {
token = refreshRingToken() // avoid predictable disconnects from auth expiry
} Try / catch
client, err := Dial(ctx, query)
if err != nil && err.Error() == "ring: disconnect" {
if client, err = reconnectWithBackoff(ctx, query); err != nil {
return err
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Expired or revoked Ring refresh token causing the server to close the socket; flaky network/NAT timeouts mid-session; Ring service incident.
Related errors
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/1a9d482bb3bf2196.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/ring/client.go:128
prod.FormatName = "ring/webrtc"
prod.Mode = core.ModeActiveProducer
prod.Protocol = "ws"
prod.URL = rawURL
client.wsClient.onMessage = func(msg WSMessage) {
client.onWSMessage(msg)
}
client.wsClient.onError = func(err error) {
// fmt.Printf("ring: error: %s\n", err.Error())
client.Stop()
client.connected.Done(err)
}
client.wsClient.onClose = func() {
// fmt.Println("ring: disconnect")
client.Stop()
client.connected.Done(errors.New("ring: disconnect"))
}
prod.Listen(func(msg any) {
switch msg := msg.(type) {
case *pion.ICECandidate:
_ = sendOffer.Wait()
iceCandidate := msg.ToJSON()
// skip empty ICE candidates
if iceCandidate.Candidate == "" {
return
}
icePayload := map[string]interface{}{
"ice": iceCandidate.Candidate,
"mlineindex": iceCandidate.SDPMLineIndex,
}View on GitHub (pinned to c245815e75)