AlexxIT/go2rtc · error

authentication failed after

Error message

authentication failed after %d retries

What it means

Ring API client exhausted all retry attempts because every request came back HTTP 401 Unauthorized. Before each retry the client clears its cached OAuth token and session, but re-authentication still produced a token the Ring server rejects, so it gives up after maxRetries. This means your credentials or auth flow are fundamentally being rejected, not just rate-limited.

Solutions

  1. Verify the refresh token / credentials in your config are current and re-authenticate from scratch (delete cached token and log in again)
  2. Check that hardware_id is a valid 16-char hex-style UUID; generate a fresh one if invalid
  3. Confirm your Ring account has no pending 2FA/password-change that invalidates old tokens
  4. Check network path for proxies intercepting Authorization headers
  5. Update the library in case Ring changed its auth endpoints

Example fix

// before
c.SetRefreshToken(staleToken) // expired, causes 401 loops
// after
tok, err := fetchFreshRefreshToken(username, password)
if err != nil { return err }
c.SetRefreshToken(tok)
Defensive patterns

Strategy: retry

Validate before calling

// preflight: verify credentials can mint a token
if refreshToken == "" || len(hardwareID) != 16 {
    return errors.New("invalid ring credentials or hardware_id")
}
_ = client.EnsureAuth() // fails fast before real API calls

Type guard

func isAuthExhausted(err error) bool {
    return err != nil && strings.Contains(err.Error(), "authentication failed after")
}

Try / catch

resp, err := client.GetDevices()
if isAuthExhausted(err) {
    // re-authenticate from scratch with fresh credentials
    client.ResetAuth()
    resp, err = client.GetDevices()
}

Prevention

When it happens

Trigger: Calling any RingApi.Request() method when the server returns 401 on every attempt of the retry loop — typically because the refresh token in config is expired/revoked, the username/password is wrong, or Ring has flagged the hardware_id/user-agent and blocks token issuance.

Common situations: Ring account password changed or 2FA re-auth required, invalid or revoked refresh_token in .env/config, incorrect hardware_id format, Ring revoking unofficial API access, or a proxy/firewall stripping the Authorization header.

Understand the failure class

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/c7bc5221ca6d8233. Report an issue: GitHub.

Appendix: source

Thrown at pkg/ring/api.go:460

			continue
		}
		defer resp.Body.Close()

		responseBody, err = io.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("failed to read response body: %w", err)
		}

		// Handle 401 by refreshing auth and retrying
		if resp.StatusCode == http.StatusUnauthorized {
			// Reset token to force refresh
			c.authMutex.Lock()
			c.authToken = nil
			c.tokenExpiry = time.Time{} // Reset token expiry
			c.authMutex.Unlock()

			if attempt == maxRetries {
				return nil, fmt.Errorf("authentication failed after %d retries", maxRetries)
			}

			// By 401 with Auth AND Session start over
			c.sessionMutex.Lock()
			c.session = nil
			c.sessionExpiry = time.Time{} // Reset session expiry
			c.sessionMutex.Unlock()

			if err := c.ensureSession(); err != nil {
				return nil, fmt.Errorf("failed to refresh session: %w", err)
			}

			req.Header.Set("Authorization", "Bearer "+c.authToken.AccessToken)
			continue
		}

		// Handle 404 error with hardware_id reference - session issue
		if resp.StatusCode == 404 && strings.Contains(url, clientAPIBaseURL) {

View on GitHub (pinned to c245815e75)