{"record":{"id":"954d59fef80598c2","repo":"SubtitleEdit/subtitleedit","slug":"perhaps-billing-is-not-enabled-or-api-not-enable-954d59","errorCode":null,"errorMessage":"\"Perhaps billing is not enabled (or API not enabled or API key is invalid)?\"","messagePattern":"\"Perhaps billing is not enabled \\(or API not enabled or API key is invalid\\)\\?\"","errorType":"exception","errorClass":"OcrException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Ocr/Engines/GoogleVisionOcr.cs","lineNumber":195,"sourceCode":"        {\n            new DataContractJsonSerializer(typeof(RequestBody)).WriteObject(memoryStream, requestBody);\n            requestBodyString = Encoding.Default.GetString(memoryStream.ToArray());\n        }\n\n        // 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)","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Ocr/Engines/GoogleVisionOcr.cs#L177-L213","documentation":"Thrown by GoogleVisionOcr.Ocr on HTTP 403 from Cloud Vision. 403 means authenticated but forbidden — billing disabled, the Vision API not enabled on the project, or the key lacks permission. Note the message text itself contains literal escaped quote characters (\\\"...\\\"), which is a cosmetic bug in the source string, but the exception type (OcrException) and HTTP code are the meaningful signal.","triggerScenarios":"result.StatusCode == 403 after posting to the Vision endpoint with a key Google accepted syntactically.","commonSituations":"Project has Cloud Vision API enabled but no billing account linked; API enabled in a different project than the key; key restricted away from Vision; quota exhausted.","solutions":["Link a billing account to the Google Cloud project (Vision requires billing even for the free tier).","Enable the Cloud Vision API in the same project that issued the key.","Remove key restrictions that block Vision, or create an unrestricted key for testing.","Check the API console for quota/abuse blocks."],"exampleFix":"// console steps\n// 1. APIs & Services -> Library -> enable \"Cloud Vision API\"\n// 2. Billing -> link a billing account\n// 3. APIs & Services -> Credentials -> recreate the key","handlingStrategy":"validation","validationCode":"// Pre-flight: warn if billing not set. The API itself must confirm at runtime.\nif (string.IsNullOrWhiteSpace(apiKey)) throw new OcrException(\"Missing Google Vision API key.\");","typeGuard":"null","tryCatchPattern":"try { return await ocr.Ocr(bmp, key, lang, ct); }\ncatch (OcrException ex) when (ex.Message.Contains(\"billing\")) {\n    logger.Error(\"Enable billing + Vision API on the Google project, then retry.\"); throw; }","preventionTips":["Enable billing and the Cloud Vision API before generating the key.","Use the same project for the key and the API enablement.","Keep an unrestricted test key to isolate restriction-related 403s."],"tags":["subtitle-edit","ocr","google-vision","billing","http-403","authorization"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}