laurent22/joplin · warning · Error

Could not check for updates. The server rate limit has been

Error message

Could not check for updates. The server rate limit has been exceeded — this is a temporary issue, please try again later.

What it means

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.

Source

Thrown at packages/app-desktop/utils/checkForUpdatesUtils.ts:145

			output.push(r);
		}
		return output.join('\n');
	}

	const output: Release = {
		version: version,
		downloadUrl: downloadUrl,
		notes: cleanUpReleaseNotes(fullReleaseNotes),
		pageUrl: release.html_url,
		prerelease: release.prerelease,
	};

	return output;
};

export const handleReleaseResponseError = (status: number, responseText: string): never => {
	if ((status === 403 || status === 429) && responseText.toLowerCase().includes('rate limit')) {
		throw new Error('Could not check for updates. The server rate limit has been exceeded — this is a temporary issue, please try again later.');
	}

	throw new Error(`Could not check for updates. Please try again later (Error ${status}).`);
};

View on GitHub (pinned to 2654b33620)

Solutions

  1. Wait and retry later — the limit resets on the hour.
  2. Authenticate the update-check request (GitHub token) to raise the ceiling if the updater supports it.
  3. Reduce update-check frequency in settings.
  4. If behind a shared NAT/CI, use an authenticated proxy or a cached release feed.

Example fix

// before
throw new Error('Could not check for updates. The server rate limit has been exceeded — this is a temporary issue, please try again later.');

// after — include retry hint with reset time if available
throw new Error(_('Could not check for updates. GitHub rate limit exceeded (resets at %s). Please try again later.', rateLimitReset));
Defensive patterns

Strategy: retry

Validate before calling

const maxChecksPerHour = 5;
if (checksThisHour >= maxChecksPerHour) {
  // skip this check to avoid hitting the rate limit
  return;
}
await checkForUpdates();

Type guard

function isRateLimitStatus(status: number, body: string): boolean {
  return (status === 403 || status === 429) && body.toLowerCase().includes('rate limit');
}

Try / catch

try {
  await checkForUpdates();
} catch (e) {
  if (e.message.includes('rate limit')) {
    // schedule a retry after the GitHub reset window (~1 hr)
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/ab213f75c05e9f64. Report an issue: GitHub.