{"record":{"id":"ab213f75c05e9f64","repo":"laurent22/joplin","slug":"could-not-check-for-updates-the-server-rate-limit","errorCode":null,"errorMessage":"Could not check for updates. The server rate limit has been exceeded — this is a temporary issue, please try again later.","messagePattern":"Could not check for updates\\. The server rate limit has been exceeded — this is a temporary issue, please try again later\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/app-desktop/utils/checkForUpdatesUtils.ts","lineNumber":145,"sourceCode":"\t\t\toutput.push(r);\n\t\t}\n\t\treturn output.join('\\n');\n\t}\n\n\tconst output: Release = {\n\t\tversion: version,\n\t\tdownloadUrl: downloadUrl,\n\t\tnotes: cleanUpReleaseNotes(fullReleaseNotes),\n\t\tpageUrl: release.html_url,\n\t\tprerelease: release.prerelease,\n\t};\n\n\treturn output;\n};\n\nexport const handleReleaseResponseError = (status: number, responseText: string): never => {\n\tif ((status === 403 || status === 429) && responseText.toLowerCase().includes('rate limit')) {\n\t\tthrow new Error('Could not check for updates. The server rate limit has been exceeded — this is a temporary issue, please try again later.');\n\t}\n\n\tthrow new Error(`Could not check for updates. Please try again later (Error ${status}).`);\n};\n","sourceCodeStart":127,"sourceCodeEnd":150,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-desktop/utils/checkForUpdatesUtils.ts#L127-L150","documentation":"Thrown by handleReleaseResponseError when the GitHub releases API responds 403 or 429 AND the response body contains 'rate limit'. This is GitHub's unauthenticated/low-authenticated rate-limit response; the updater translates it into a user-facing temporary-failure message.","triggerScenarios":"checkForUpdates fetches GitHub's releases endpoint; the server returns 403/429 with a body mentioning 'rate limit' (case-insensitive). handleReleaseResponseError matches the condition and throws.","commonSituations":"Many clients behind one NAT/IP checking updates simultaneously; an unauthenticated request after the 60/hr unauthenticated ceiling; CI/shared environments hammering the API; a proxy returning a 403 with 'rate limit' in its error page.","solutions":["Wait and retry later — the limit resets on the hour.","Authenticate the update-check request (GitHub token) to raise the ceiling if the updater supports it.","Reduce update-check frequency in settings.","If behind a shared NAT/CI, use an authenticated proxy or a cached release feed."],"exampleFix":"// before\nthrow new Error('Could not check for updates. The server rate limit has been exceeded — this is a temporary issue, please try again later.');\n\n// after — include retry hint with reset time if available\nthrow new Error(_('Could not check for updates. GitHub rate limit exceeded (resets at %s). Please try again later.', rateLimitReset));","handlingStrategy":"retry","validationCode":"const maxChecksPerHour = 5;\nif (checksThisHour >= maxChecksPerHour) {\n  // skip this check to avoid hitting the rate limit\n  return;\n}\nawait checkForUpdates();","typeGuard":"function isRateLimitStatus(status: number, body: string): boolean {\n  return (status === 403 || status === 429) && body.toLowerCase().includes('rate limit');\n}","tryCatchPattern":"try {\n  await checkForUpdates();\n} catch (e) {\n  if (e.message.includes('rate limit')) {\n    // schedule a retry after the GitHub reset window (~1 hr)\n  } else throw e;\n}","preventionTips":["Throttle update checks (do not poll on every app launch in quick succession).","Authenticate the GitHub request to raise the rate ceiling where supported.","Cache the last-known release and only re-check on a sensible interval."],"tags":["desktop","updates","network","rate-limit","github"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}