{"record":{"id":"3a0945143ba9d8ef","repo":"AlexxIT/go2rtc","slug":"request-failed-after-d-retries-w","errorCode":null,"errorMessage":"request failed after %d retries: %w","messagePattern":"request failed after (.+?) retries: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/ring/api.go","lineNumber":439,"sourceCode":"\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\n\t// Set headers\n\treq.Header.Set(\"Authorization\", \"Bearer \"+c.authToken.AccessToken)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"Accept\", \"application/json\")\n\treq.Header.Set(\"hardware_id\", c.hardwareID)\n\treq.Header.Set(\"User-Agent\", \"android:com.ringapp\")\n\n\t// Make request with retries\n\tvar resp *http.Response\n\tvar responseBody []byte\n\n\tfor attempt := 0; attempt <= maxRetries; attempt++ {\n\t\tresp, err = c.httpClient.Do(req)\n\t\tif err != nil {\n\t\t\tif attempt == maxRetries {\n\t\t\t\treturn nil, fmt.Errorf(\"request failed after %d retries: %w\", maxRetries, err)\n\t\t\t}\n\t\t\ttime.Sleep(5 * time.Second)\n\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()","sourceCodeStart":421,"sourceCodeEnd":457,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/ring/api.go#L421-L457","documentation":"RingApi.Request retries httpClient.Do up to maxRetries times, sleeping 5 seconds between attempts. If every attempt fails with a transport-level error (connection refused, DNS failure, TLS error, timeout), it gives up and returns 'request failed after N retries' wrapping the last error. The request reached neither a successful nor an HTTP-level response.","triggerScenarios":"Calling any RingApi.Request when the Ring API endpoint is unreachable for all attempts: network outage, DNS failure, server down, TLS handshake failure, or persistent timeouts.","commonSituations":"Ring service outage or maintenance; corporate proxy/firewall blocking api.ring.com; local network drop (Wi-Fi down); DNS misconfiguration; timeouts because the client has no deadline configured and the route is saturated.","solutions":["Check basic network connectivity and DNS resolution for the Ring API host (curl/ping the endpoint)","Inspect the wrapped error (errors.Unwrap) to identify the root cause: connection refused vs timeout vs TLS","Increase maxRetries or backoff in the client config if the endpoint is intermittently reachable","Configure a proxy (HTTP_PROXY/HTTPS_PROXY) if required by your network","Retry later if it's a Ring-side outage; check Ring status/community forums"],"exampleFix":"// before\nresp, err := c.httpClient.Do(req) // no timeout, hangs then fails all retries\n// after\nc.httpClient.Timeout = 30 * time.Second\nresp, err := c.httpClient.Do(req)\nif err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        return nil, fmt.Errorf(\"ring api timed out after %d retries: %w\", maxRetries, err)\n    }\n    return nil, fmt.Errorf(\"request failed after %d retries: %w\", maxRetries, err)\n}","handlingStrategy":"retry","validationCode":"// Go: preflight reachability check before important calls\nfunc reachable(host string) error {\n    conn, err := net.DialTimeout(\"tcp\", host+\":443\", 5*time.Second)\n    if err != nil {\n        return err\n    }\n    return conn.Close()\n}","typeGuard":null,"tryCatchPattern":"// Go: exponential backoff around the library call\nvar resp []byte\nerr := retry.Do(func() error {\n    var e error\n    resp, e = api.Request(\"GET\", url, nil)\n    if e != nil && strings.Contains(e.Error(), \"request failed after\") {\n        return retry.Transient(e)\n    }\n    return retry.Permanent(e)\n}, retry.Attempts(3), retry.DelayType(retry.BackOffDelay))","preventionTips":["Set an explicit http.Client.Timeout so failures surface quickly and predictably","Verify DNS/firewall/proxy settings for api.ring.com before deploying","Distinguish permanent errors (DNS, TLS) from transient ones before retrying","Monitor Ring service status and back off during known outages"],"tags":["network","http","retry","timeout"],"backgroundTag":"http-request-failed","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"}