SubtitleEdit/subtitleedit · error · OcrException
An error occurred calling Cloud Vision API - status code: {r
Error message
An error occurred calling Cloud Vision API - status code: {result.StatusCode} What it means
OcrException catch-all for any non-success status from the Cloud Vision call that is not 400 or 403 — typically 429 (rate limit), 500/502/503 (server-side), 408/504 (timeout), or 401. Only the StatusCode is included; the response body is discarded, so the real reason must be inferred from the code.
Source
Thrown at src/libuilogic/Ocr/Service/GoogleCloudVisionApi.cs:214
// Do request
var uri = $"?key={_apiKey}";
string content;
try
{
var result = _httpClient.PostAsync(uri, new StringContent(requestBodyString)).Result;
if ((int)result.StatusCode == 400)
{
throw new OcrException("API key invalid (or perhaps billing/API is not enabled)?");
}
if ((int)result.StatusCode == 403)
{
throw new OcrException("\"Perhaps billing is not enabled (or API not enabled or API key is invalid)?\"");
}
if (!result.IsSuccessStatusCode)
{
throw new OcrException($"An error occurred calling Cloud Vision API - status code: {result.StatusCode}");
}
content = result.Content.ReadAsStringAsync().Result;
}
catch (WebException webException)
{
var message = string.Empty;
if (webException.Message.Contains("(400) Bad Request"))
{
message = "API key invalid (or perhaps billing is not enabled)?";
}
else if (webException.Message.Contains("(403) Forbidden."))
{
message = "Perhaps billing is not enabled (or API key is invalid)?";
}
throw new OcrException(message, webException);
}
View on GitHub (pinned to 17a9f07487)
Solutions
- On 429, throttle to stay under your per-minute quota and retry with exponential backoff.
- On 5xx/504, retry with backoff (these are transient); Vision API SLA covers most such cases.
- Log result.Content so the next time you have Google's structured error reason instead of just a status code.
- For batch work, add jitter and cap concurrency to avoid synchronized bursts.
Example fix
// before
if (!result.IsSuccessStatusCode)
throw new OcrException($"An error occurred calling Cloud Vision API - status code: {result.StatusCode}");
// after
if (!result.IsSuccessStatusCode)
{
var body = result.Content.ReadAsStringAsync().Result;
throw new OcrException($"Cloud Vision API {(int)result.StatusCode} {result.StatusCode}. Body: {body}");
} Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
async Task<string> CallVisionWithRetry(...)
{
for (int attempt = 0; attempt < 4; attempt++)
{
try { return await CallVision(...); }
catch (OcrException ex) when (IsTransient(ex))
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
}
throw new OcrException("Vision API failed after retries.");
} Prevention
- Implement exponential backoff with jitter for 429/5xx.
- Log the response body, not just the status code — Vision returns structured error reasons.
- Throttle concurrency to stay under your per-minute quota during batch OCR.
When it happens
Trigger: Hitting Vision API during an outage (5xx), exceeding per-minute request quotas (429), network blips (504), or a transient backend error.
Common situations: Batch OCR of many images hitting the 1800 req/min default quota; Google regional outage; slow upload on large images causing gateway timeouts.
Related errors
- An error occurred calling Cloud Vision API - status code: {r
- Lens returned a non-200 status code
- API key invalid (or perhaps billing/API is not enabled)?
- "Perhaps billing is not enabled (or API not enabled or API k
- Ollama returned {(int)resp.StatusCode}: {json}
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/a188fc580679602a.
Report an issue: GitHub.