AlexxIT/go2rtc · error
ring: close
Error message
ring: close
What it means
When the ring WebSocket receives a "close" message from the server, the client stops itself and completes the pending connection with this "ring: close" error, indicating the remote side intentionally closed the session.
Solutions
- Refresh the Ring refresh_token and reconnect
- Check for other active sessions using the same camera
- Add automatic reconnect logic with fresh credentials
- Retry after a delay in case of server-side throttling
Example fix
// before
client, err := Dial(ctx, query) // fails with "ring: close"
// after
if err != nil && errors.Is(err, errRingClose) {
query.Set("refresh_token", getFreshToken())
client, err = Dial(ctx, query)
} Defensive patterns
Strategy: retry
Validate before calling
if time.Since(tokenRefreshedAt) > maxTokenAge {
query.Set("refresh_token", refreshRingToken()) // stale tokens cause server-initiated close
} Try / catch
client, err := Dial(ctx, query)
if err != nil && err.Error() == "ring: close" {
query.Set("refresh_token", getFreshToken())
client, err = Dial(ctx, query)
} Prevention
- Keep the refresh token fresh; server closes sessions on auth expiry
- Avoid concurrent streams for the same camera
- Add a delay before reconnecting to respect server-side throttling
When it happens
Trigger: Ring's server sends a WebSocket message of type "close" — e.g. session rejected, token expired, concurrent session limit, or server-side teardown of the stream.
Common situations: Expired refresh token invalidating the live session; opening a second stream for the same camera; Ring server maintenance or rate limiting.
Related errors
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/0c10ec1f7f5c4fb7.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/ring/client.go:301
if err := json.Unmarshal(rawMsg, &msg); err != nil {
break
}
// Skip empty candidates
if msg.Body.Ice == "" {
break
}
if err := prod.AddCandidate(msg.Body.Ice); err != nil {
c.Stop()
c.connected.Done(err)
return
}
}
case "close":
c.Stop()
c.connected.Done(errors.New("ring: close"))
case "pong":
// Ignore
}
}
func (c *Client) GetMedias() []*core.Media {
return c.prod.GetMedias()
}
func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.Receiver, error) {
return c.prod.GetTrack(media, codec)
}
func (c *Client) AddTrack(media *core.Media, codec *core.Codec, track *core.Receiver) error {
if webrtcProd, ok := c.prod.(*webrtc.Conn); ok {
if media.Kind == core.KindAudio {
// Enable speakerView on GitHub (pinned to c245815e75)