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

  1. Refresh the Ring refresh_token and dial again
  2. Implement reconnect logic with backoff around Dial/usage
  3. Check network stability / firewall blocking the WebSocket
  4. 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

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)