{"record":{"id":"d932b373a9f3df65","repo":"AlexxIT/go2rtc","slug":"nest-max-retries-exceeded","errorCode":null,"errorMessage":"nest: max retries exceeded","messagePattern":"nest: max retries exceeded","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/nest/api.go","lineNumber":227,"sourceCode":"\t\t\t\tAnswer         string    `json:\"answerSdp\"`\n\t\t\t\tExpiresAt      time.Time `json:\"expiresAt\"`\n\t\t\t\tMediaSessionID string    `json:\"mediaSessionId\"`\n\t\t\t} `json:\"results\"`\n\t\t}\n\n\t\tif err = json.NewDecoder(res.Body).Decode(&resv); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\ta.StreamProjectID = projectID\n\t\ta.StreamDeviceID = deviceID\n\t\ta.StreamSessionID = resv.Results.MediaSessionID\n\t\ta.StreamExpiresAt = resv.Results.ExpiresAt\n\n\t\treturn resv.Results.Answer, nil\n\t}\n\n\treturn \"\", errors.New(\"nest: max retries exceeded\")\n}\n\nfunc (a *API) refreshToken() error {\n\t// Get the cached API with matching token to get credentials\n\tvar refreshKey string\n\tcacheMu.Lock()\n\tfor key, api := range cache {\n\t\tif api.Token == a.Token {\n\t\t\trefreshKey = key\n\t\t\tbreak\n\t\t}\n\t}\n\tcacheMu.Unlock()\n\n\tif refreshKey == \"\" {\n\t\treturn errors.New(\"nest: unable to find cached credentials\")\n\t}\n","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/nest/api.go#L209-L245","documentation":"ExchangeSDP retries the SDP exchange a limited number of times (e.g. to wait until the camera becomes available); if every attempt fails without producing an answer SDP, it gives up and returns this sentinel error. It is an exhaustion signal rather than a specific HTTP failure — each attempt had its own failure reason.","triggerScenarios":"Calling ExchangeSDP when all retry attempts fail (camera busy/offline, token invalid, or transient Nest cloud errors persist across the whole retry window).","commonSituations":"Camera perpetually busy because another client holds the stream; camera offline/unreachable; credentials never refreshed so every attempt 401s; retry budget too small for slow cameras.","solutions":["Fix the underlying per-attempt failure (check for an earlier 'nest: wrong status' error or logs) — refresh tokens, free the camera, restore connectivity.","Stop competing stream clients so the camera becomes available within the retry window.","Increase the retry count/delay if the camera is slow to become ready.","Verify camera power/network status; a camera that never answers will always exhaust retries."],"exampleFix":"// before\n// 3 quick retries, camera still busy\nanswer, err := api.ExchangeSDP(ctx, offer)\n\n// after\n// stop other viewers first, then retry with longer window\nstopOtherViewers(cameraID)\nanswer, err := api.ExchangeSDP(ctx, offer)","handlingStrategy":"fallback","validationCode":"// check camera reachability before attempting SDP exchange\nif !cameraOnline(cameraID) {\n    return errors.New(\"camera offline; skipping SDP exchange\")\n}","typeGuard":null,"tryCatchPattern":"answer, err := api.ExchangeSDP(ctx, offer)\nif err != nil && strings.Contains(err.Error(), \"max retries exceeded\") {\n    // stop competing viewers, wait, then fall back to snapshot stream\n    stopOtherViewers(cameraID)\n    return fallbackToSnapshotStream(ctx, cameraID)\n}","preventionTips":["Ensure exclusive access to the camera stream before connecting.","Alert on repeated retry exhaustion — it usually signals camera offline or busy.","Increase retry window for slow cameras if acceptable for UX.","Check camera power/network health as part of the runbook."],"tags":["nest","webrtc","retry","timeout"],"backgroundTag":"request-timeout","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}