Kareadita/Kavita · error · KavitaException
GitHub API access denied. Please try again later.
Error message
GitHub API access denied. Please try again later.
What it means
Thrown in the same 403 handler as the rate-limit error, but only when rateLimit.IsExhausted is false. This means GitHub returned 403 for a non-quota reason: the token lacks scope, the repo access was revoked, an abuse/secondary rate limit triggered, or the IP is blocked. Kavita cannot distinguish these from the response, so it surfaces a generic 'access denied, try later' message.
Source
Thrown at Kavita.Services/ReadingLists/CblGithubService.cs:220
catch (FlurlHttpException ex) when (ex.StatusCode == 403)
{
var rateLimit = ex.Call?.Response != null
? ex.Call.Response.GetRateLimit()
: new GithubRateLimitDto();
if (rateLimit.IsExhausted)
{
var resetsIn = rateLimit.ResetsAtUtc.HasValue
? $" Resets at {rateLimit.ResetsAtUtc.Value:HH:mm} UTC."
: string.Empty;
_logger.LogWarning("GitHub API rate limit exhausted.{ResetsIn}", resetsIn);
throw new KavitaException(
$"GitHub API rate limit exhausted.{resetsIn} Cached data may still be available.");
}
_logger.LogWarning(ex, "GitHub API returned 403 for CBL repo");
throw new KavitaException("GitHub API access denied. Please try again later.");
}
catch (FlurlHttpException ex) when (ex.StatusCode == 404)
{
_logger.LogWarning("CBL repo path not found: {Path}", path.Sanitize());
throw new KavitaException($"Path not found in CBL repository: {path}");
}
}
private CblRepoCache LoadCache()
{
var cachePath = GetCacheFilePath();
if (!File.Exists(cachePath)) return new CblRepoCache();
try
{
var json = File.ReadAllText(cachePath);
return JsonSerializer.Deserialize<CblRepoCache>(json) ?? new CblRepoCache();
}View on GitHub (pinned to 9c3e540000)
Solutions
- Regenerate the GitHub token with at least 'public_repo' (or no-scope public read) and reconfigure it in GithubHeaders.
- Wait ~1 minute and retry to clear a secondary/abuse rate limit.
- Check the FlurlHttpException.Call.Response headers/body in logs to confirm whether it is auth, scope, or abuse.
- Fall back to the cached CblRepoCache (BrowseRepo without forceRefresh).
Example fix
// before
var item = await cblGithubService.GetFileContent(filePath); // 403 -> access denied
// after
try { return await cblGithubService.GetFileContent(filePath); }
catch (KavitaException) when (await cblGithubService.HasCacheFor(filePath))
{
return await cblGithubService.GetCachedFileContent(filePath);
} Defensive patterns
Strategy: retry
Validate before calling
// Non-quota 403 can't be reliably pre-validated; ensure token scope instead. // Verify token has public_repo read before configuring GithubHeaders.
Try / catch
try { return await cblGithubService.GetFileContent(filePath); }
catch (KavitaException ex) when (ex.Message.Contains("access denied"))
{ await Task.Delay(TimeSpan.FromSeconds(30)); return await cblGithubService.GetFileContent(filePath); } // one retry for abuse limit Prevention
- Use a PAT with correct public read scope.
- Throttle requests to avoid GitHub abuse limits.
- Inspect FlurlHttpException.Call.Response in logs to classify the 403.
- Fall back to cache when the live API is inaccessible.
When it happens
Trigger: FetchDirectoryFromGithub gets a FlurlHttpException with StatusCode 403 whose X-RateLimit-Remaining is non-zero. Common when a configured PAT lacks 'public_repo' read scope, when GitHub triggered an abuse detection limit, or when the CBL repo visibility changed.
Common situations: An expired or scope-stripped personal access token; corporate proxy/firewall rewriting GitHub responses; GitHub's abuse rate limiting after bursts of requests that are individually under quota; or the DieselTech/CBL-ReadingLists repo was made private.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- No download URL available for {filePath}
- GitHub API rate limit exhausted.{resetsIn} Cached data may s
- Path not found in CBL repository: {path}
- url-blocked-address
- Kavita has already tried to fetch from {sanitizedBaseUrl} an
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/e0dca3466424f80d.
Report an issue: GitHub.