{"record":{"id":"b7ed123a52cdcf6a","repo":"SubtitleEdit/subtitleedit","slug":"perhaps-billing-is-not-enabled-or-api-key-is-inv","errorCode":null,"errorMessage":"\"Perhaps billing is not enabled (or API key is invalid)?\"","messagePattern":"\"Perhaps billing is not enabled \\(or API key is invalid\\)\\?\"","errorType":"exception","errorClass":"Exception","httpStatus":403,"severity":"error","filePath":"src/libuilogic/AutoTranslate/GoogleTranslateV2.cs","lineNumber":80,"sourceCode":"                    try\n                    {\n                        Error = await result.Content.ReadAsStringAsync(cancellationToken);\n                        SeLogger.Error($\"Error in {StaticName}.Translate: \" + Error);\n                    }\n                    catch\n                    {\n\n                        // ignore\n                    }\n                }\n\n                if ((int)result.StatusCode == 400)\n                {\n                    throw new Exception(\"API key invalid (or perhaps billing is not enabled)?\");\n                }\n                if ((int)result.StatusCode == 403)\n                {\n                    throw new Exception(\"\\\"Perhaps billing is not enabled (or API key is invalid)?\\\"\");\n                }\n\n                if (!result.IsSuccessStatusCode)\n                {\n                    throw new Exception($\"An error occurred calling GT translate - status code: {result.StatusCode}\");\n                }\n\n                content = await result.Content.ReadAsStringAsync(cancellationToken);\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 API/billing is not enabled)?\";\n                }\n                else if (webException.Message.Contains(\"(403) Forbidden.\"))\n                {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/libuilogic/AutoTranslate/GoogleTranslateV2.cs#L62-L98","documentation":"Thrown by GoogleTranslateV2 when the POST returns HTTP 403 Forbidden. Google returns 403 when billing is not enabled, the API is not enabled for the project, or the API key is invalid/restricted. Note the message string contains literal escaped quotes (\\\"...\\\"), which is a copy-paste artifact in the source.","triggerScenarios":"Translate() posts with the key; result.StatusCode is 403. The Error field was already populated and logged at lines 62-66 if reading the body succeeded.","commonSituations":"Billing disabled on the GCP project; Translation API not enabled; API key restricted by IP/referrer that does not match the caller; key deleted or disabled in the console; daily quota exceeded can also surface as 403.","solutions":["Enable billing on the GCP project (Cloud Translation requires an active billing account).","Enable the Cloud Translation API in APIs & Services.","Check the API key restrictions in the console and loosen them if needed.","Verify daily quota has not been exhausted in the GCP quotas page."],"exampleFix":"// before\nif ((int)result.StatusCode == 403)\n{\n    throw new Exception(\"\\\"Perhaps billing is not enabled (or API key is invalid)?\\\"\");\n}\n\n// after - remove the stray escaped quotes and include Google's error body\nif ((int)result.StatusCode == 403)\n{\n    throw new Exception($\"GoogleTranslateV2 returned 403 (billing not enabled, API not enabled, or key invalid/restricted): {Error}\");\n}","handlingStrategy":"validation","validationCode":"// Check that the GCP project is likely usable: key present + a usage probe\nvar key = Configuration.Settings.Tools.GoogleApiV2Key;\nif (string.IsNullOrWhiteSpace(key))\n    throw new InvalidOperationException(\"Google V2 API key missing.\");\n// Quick probe: list languages (cheap) to confirm billing/API/key state\nusing var c = new HttpClient();\nvar resp = await c.GetAsync($\"https://translation.googleapis.com/language/translate/v2/languages?key={key}\");\nif ((int)resp.StatusCode == 403)\n    throw new InvalidOperationException(\"Google returned 403 on a probe - enable billing and the Translation API, or fix key restrictions.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    return await googleV2.Translate(text, src, tgt, token);\n}\ncatch (Exception ex) when (ex.Message.Contains(\"billing is not enabled\"))\n{\n    throw new InvalidOperationException(\"Google returned 403 - billing or the Translation API is not enabled, or the key is restricted.\", ex);\n}","preventionTips":["Enable billing before enabling the API - Google returns 403 if billing is off even with a valid key.","Do a cheap /languages probe on setup to catch 403 early with an actionable message.","Avoid IP-restricting the key to an IP that changes (e.g. dynamic NAT).","Note the stray quotes in the original message are a source bug; patch the message for clarity."],"tags":["api","auth","billing","translation","google","configuration"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}