{"record":{"id":"48f04be1f0215160","repo":"SubtitleEdit/subtitleedit","slug":"perhaps-billing-is-not-enabled-or-api-not-enable","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":403,"severity":"error","filePath":"src/libuilogic/Ocr/Service/GoogleCloudVisionApi.cs","lineNumber":209,"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 = _httpClient.PostAsync(uri, new StringContent(requestBodyString)).Result;\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 = result.Content.ReadAsStringAsync().Result;\n            }\n            catch (WebException webException)\n            {\n                var message = string.Empty;\n                if (webException.Message.Contains(\"(400) Bad Request\"))\n                {\n                    message = \"API key invalid (or perhaps billing is not enabled)?\";\n                }\n                else if (webException.Message.Contains(\"(403) Forbidden.\"))\n                {","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/libuilogic/Ocr/Service/GoogleCloudVisionApi.cs#L191-L227","documentation":"OcrException thrown when the Cloud Vision REST call returns HTTP 403. 403 means authentication succeeded but access is forbidden — most often because billing is disabled, the Vision API is not enabled on the project, or the API key has API restrictions excluding Vision. Note the message text accidentally contains escaped quotes from the source literal.","triggerScenarios":"Posting to Vision with a valid-format key on a project where Cloud Vision API is disabled, billing is disabled, the key is restricted to other APIs, or per-key quotas were exceeded.","commonSituations":"Free-tier project where billing was never enabled; trial credits exhausted; API key restricted to 'Maps JavaScript API' only; project suspended by Google.","solutions":["In GCP Console, open Billing and link an active billing account to the project.","Under APIs & Services, enable 'Cloud Vision API' for the project.","Edit the API key restrictions to allow Cloud Vision API (or remove restrictions for testing).","Check IAM/APIs & Services > Quotas for 403-driven quota caps if billing is already on."],"exampleFix":"// before\nthrow new OcrException(\"\\\"Perhaps billing is not enabled (or API not enabled or API key is invalid)?\\\"\");\n\n// after — fix the stray quotes and keep the body for triage\nthrow new OcrException($\"Cloud Vision API returned 403 Forbidden. Billing/API not enabled or key restricted. Body: {result.Content.ReadAsStringAsync().Result}\");","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try { content = CallVision(...); }\ncatch (OcrException ex) when (ex.Message.Contains(\"billing\") || ex.Message.Contains(\"403\"))\n{ SeLogger.Error(ex, \"Enable billing + Cloud Vision API in GCP, then retry.\"); throw; }","preventionTips":["Enable Cloud Vision API AND link billing before first use.","Restrict the API key to Vision only (least privilege) but never to the wrong API set.","Surface Google's 403 body so the user sees 'API not enabled' vs 'billing disabled'."],"tags":["ocr","google-cloud-vision","http-403","billing","authorization"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}