{"record":{"id":"c7bc5221ca6d8233","repo":"AlexxIT/go2rtc","slug":"authentication-failed-after-d-retries","errorCode":null,"errorMessage":"authentication failed after %d retries","messagePattern":"authentication failed after (.+?) retries","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/ring/api.go","lineNumber":460,"sourceCode":"\t\t\tcontinue\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tresponseBody, err = io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to read response body: %w\", err)\n\t\t}\n\n\t\t// Handle 401 by refreshing auth and retrying\n\t\tif resp.StatusCode == http.StatusUnauthorized {\n\t\t\t// Reset token to force refresh\n\t\t\tc.authMutex.Lock()\n\t\t\tc.authToken = nil\n\t\t\tc.tokenExpiry = time.Time{} // Reset token expiry\n\t\t\tc.authMutex.Unlock()\n\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn nil, fmt.Errorf(\"authentication failed after %d retries\", maxRetries)\n\t\t\t}\n\n\t\t\t// By 401 with Auth AND Session start over\n\t\t\tc.sessionMutex.Lock()\n\t\t\tc.session = nil\n\t\t\tc.sessionExpiry = time.Time{} // Reset session expiry\n\t\t\tc.sessionMutex.Unlock()\n\n\t\t\tif err := c.ensureSession(); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to refresh session: %w\", err)\n\t\t\t}\n\n\t\t\treq.Header.Set(\"Authorization\", \"Bearer \"+c.authToken.AccessToken)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Handle 404 error with hardware_id reference - session issue\n\t\tif resp.StatusCode == 404 && strings.Contains(url, clientAPIBaseURL) {","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/ring/api.go#L442-L478","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the refresh token / credentials in your config are current and re-authenticate from scratch (delete cached token and log in again)","Check that hardware_id is a valid 16-char hex-style UUID; generate a fresh one if invalid","Confirm your Ring account has no pending 2FA/password-change that invalidates old tokens","Check network path for proxies intercepting Authorization headers","Update the library in case Ring changed its auth endpoints"],"exampleFix":"// before\nc.SetRefreshToken(staleToken) // expired, causes 401 loops\n// after\ntok, err := fetchFreshRefreshToken(username, password)\nif err != nil { return err }\nc.SetRefreshToken(tok)","handlingStrategy":"retry","validationCode":"// preflight: verify credentials can mint a token\nif refreshToken == \"\" || len(hardwareID) != 16 {\n    return errors.New(\"invalid ring credentials or hardware_id\")\n}\n_ = client.EnsureAuth() // fails fast before real API calls","typeGuard":"func isAuthExhausted(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"authentication failed after\")\n}","tryCatchPattern":"resp, err := client.GetDevices()\nif isAuthExhausted(err) {\n    // re-authenticate from scratch with fresh credentials\n    client.ResetAuth()\n    resp, err = client.GetDevices()\n}","preventionTips":["Refresh tokens proactively before expiry instead of waiting for 401s","Validate refresh_token and hardware_id format at client construction","Handle password/2FA changes by re-running interactive login","Keep the library updated for Ring auth endpoint changes"],"tags":["authentication","http-401","retry-exhausted","ring"],"backgroundTag":"authentication-required","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}