{"record":{"id":"ea256b9baa8ba3b7","repo":"SubtitleEdit/subtitleedit","slug":"api-key-invalid-or-perhaps-billing-api-is-not-ena-ea256b","errorCode":null,"errorMessage":"API key invalid (or perhaps billing/API is not enabled)?","messagePattern":"API key invalid \\(or perhaps billing/API is not enabled\\)\\?","errorType":"exception","errorClass":"OcrException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Ocr/Engines/GoogleVisionOcr.cs","lineNumber":190,"sourceCode":"        requestBody.requests.Add(request);\n\n        // Convert to JSON string\n        string requestBodyString;\n        using (var memoryStream = new MemoryStream())\n        {\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        }","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Ocr/Engines/GoogleVisionOcr.cs#L172-L208","documentation":"Thrown by GoogleVisionOcr.Ocr when the Cloud Vision API responds with HTTP 400. 400 from the Vision endpoint means the request was rejected — most commonly an invalid API key passed as ?key=. The message hints billing/API enablement as a secondary cause.","triggerScenarios":"await _httpClient.PostAsync(uri, ...) returns a result with (int)result.StatusCode == 400.","commonSituations":"Wrong/typo API key; key from a different Google project; key revoked; trailing whitespace in the key; calling the wrong endpoint that returns 400 for the payload.","solutions":["Regenerate the API key in the Google Cloud Console and paste it exactly (no spaces/quotes).","Confirm the key belongs to the project where the Vision API is enabled.","Verify the key is authorised for the Cloud Vision API.","If using a restricted key, add the Vision API referrer/IP restriction that matches the call site."],"exampleFix":"// before: stale/wrong key\nvar ocr = new GoogleVisionOcr(httpClient);\nawait ocr.Ocr(bmp, \"AIzaSyOLDKEY\", \"en\", ct);\n// after: freshly minted key from the right project\nawait ocr.Ocr(bmp, Environment.GetEnvironmentVariable(\"GCV_API_KEY\"), \"en\", ct);","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(apiKey) || apiKey.Length < 30)\n    throw new OcrException(\"Google Vision API key looks invalid/empty.\");","typeGuard":"static bool LooksLikeValidGcvKey(string k) => !string.IsNullOrWhiteSpace(k) && k.StartsWith(\"AIza\", StringComparison.Ordinal);","tryCatchPattern":"null","preventionTips":["Store the key in an env var / secret manager; never hardcode.","Smoke-test the key with a one-image curl before batch OCR.","Confirm the key's project has the Vision API enabled."],"tags":["subtitle-edit","ocr","google-vision","api-key","http-400","authentication"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}