{"record":{"id":"def9c8dedba7be0e","repo":"SubtitleEdit/subtitleedit","slug":"an-error-occurred-calling-cloud-vision-api-statu-def9c8","errorCode":null,"errorMessage":"An error occurred calling Cloud Vision API - status code: {result.StatusCode}","messagePattern":"An error occurred calling Cloud Vision API - status code: (.+?)","errorType":"http","errorClass":"OcrException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Ocr/Engines/GoogleVisionOcr.cs","lineNumber":200,"sourceCode":"        // Do request\n        var uri = $\"?key={apiKey}\";\n        string content;\n        try\n        {\n            var result = await _httpClient.PostAsync(uri, new StringContent(requestBodyString), cancellationToken);\n            if ((int)result.StatusCode == 400)\n            {\n                throw new OcrException(\"API key invalid (or perhaps billing/API is not enabled)?\");\n            }\n\n            if ((int)result.StatusCode == 403)\n            {\n                throw new OcrException(\"\\\"Perhaps billing is not enabled (or API not enabled or API key is invalid)?\\\"\");\n            }\n\n            if (!result.IsSuccessStatusCode)\n            {\n                throw new OcrException($\"An error occurred calling Cloud Vision API - status code: {result.StatusCode}\");\n            }\n\n            content = await result.Content.ReadAsStringAsync(cancellationToken);\n        }\n        catch (HttpRequestException httpException)\n        {\n            throw new OcrException(\"Error calling Cloud Vision API: \" + httpException.Message, httpException);\n        }\n\n        return string.Join(Environment.NewLine, JsonToStringList(language, content));\n    }\n\n    public static List<string> JsonToStringList(string language, string content)\n    {\n        if (Configuration.Settings.Tools.OcrGoogleCloudVisionSeHandlesTextMerge)\n        {\n            var annotations = GetAnnotations(content).ToList();\n            if (annotations.Count > 0)","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Ocr/Engines/GoogleVisionOcr.cs#L182-L218","documentation":"Thrown by GoogleVisionOcr.Ocr for any non-success HTTP status that is not 400 or 403 — the catch-all branch after the two specific checks. The status code is interpolated into the message.","triggerScenarios":"result.IsSuccessStatusCode is false AND the status is neither 400 nor 403 (e.g. 401, 404, 429, 500, 503).","commonSituations":"429 rate-limit/quota exceeded; 401 unauthorized (expired OAuth-style key); 5xx transient Google outage; 404 wrong endpoint base URL configured on the HttpClient.","solutions":["Read result.StatusCode from the message and react: 429 → back off / request quota increase; 5xx → retry with jitter; 404 → fix the base address.","For rate limits, throttle calls and consider batching images per Vision request.","Confirm the HttpClient's base address is the Vision annotate endpoint.","Retry transient 5xx with exponential backoff."],"exampleFix":"// retry on 5xx/429\nvar policy = Policy.Handle<OcrException>()\n    .WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)));\nawait policy.ExecuteAsync(() => ocr.Ocr(bmp, key, lang, ct));","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"try { return await ocr.Ocr(bmp, key, lang, ct); }\ncatch (OcrException ex) when (ex.Message.Contains(\"429\") || ex.Message.Contains(\"503\")) {\n    await Task.Delay(TimeSpan.FromSeconds(backoff), ct); return await ocr.Ocr(bmp, key, lang, ct); }","preventionTips":["Inspect the embedded status code in the message and branch on it.","Throttle to stay under Vision rate limits; batch images per request.","Retry 5xx/429 with exponential backoff; surface 4xx permanently."],"tags":["subtitle-edit","ocr","google-vision","http-status","rate-limit","transient"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}