SubtitleEdit/subtitleedit · error · Exception
Perhaps billing is not enabled (or API not enabled or API ke
Error message
Perhaps billing is not enabled (or API not enabled or API key is invalid)?
What it means
Thrown inside GoogleTranslateV2's catch (WebException) block when the exception message contains '(403) Forbidden.'. Same legacy-exception path as error 50 but for the 403 case. Maps to a billing/API-not-enabled/key-invalid diagnosis. Shares the throw site and the empty-message-if-no-match pitfall with error 50.
Source
Thrown at src/libuilogic/AutoTranslate/GoogleTranslateV2.cs:102
{
throw new Exception($"An error occurred calling GT translate - status code: {result.StatusCode}");
}
content = await result.Content.ReadAsStringAsync(cancellationToken);
}
catch (WebException webException)
{
var message = string.Empty;
if (webException.Message.Contains("(400) Bad Request"))
{
message = "API key invalid (or perhaps API/billing is not enabled)?";
}
else if (webException.Message.Contains("(403) Forbidden."))
{
message = "Perhaps billing is not enabled (or API not enabled or API key is invalid)?";
}
throw new Exception(message, webException);
}
var resultList = new List<string>();
var parser = new JsonParser();
var x = (Dictionary<string, object>)parser.Parse(content);
foreach (var k in x.Keys)
{
if (x[k] is Dictionary<string, object> v)
{
foreach (var innerKey in v.Keys)
{
if (v[innerKey] is List<object> l)
{
foreach (var o2 in l)
{
if (o2 is Dictionary<string, object> v2)
{
foreach (var innerKey2 in v2.Keys)View on GitHub (pinned to 17a9f07487)
Solutions
- Enable billing and the Translation API on the GCP project.
- Loosen API key restrictions to allow the caller IP.
- Inspect the inner WebException Response body for Google's error.
- Confirm the key is not disabled in the console.
Example fix
// before
else if (webException.Message.Contains("(403) Forbidden."))
{
message = "Perhaps billing is not enabled (or API not enabled or API key is invalid)?";
}
// after - fall back to a descriptive default so the exception is never empty
var message = $"GoogleTranslateV2 transport error: {webException.Status} - {webException.Message}";
if (webException.Message.Contains("(400) Bad Request")) { message = "..."; }
else if (webException.Message.Contains("(403) Forbidden.")) { message = "..."; }
throw new Exception(message, webException); Defensive patterns
Strategy: try-catch
Validate before calling
// Probe with a cheap /languages call to catch the 403 (billing/API/key) before real work
var key = Configuration.Settings.Tools.GoogleApiV2Key;
using var c = new HttpClient();
var resp = await c.GetAsync($"https://translation.googleapis.com/language/translate/v2/languages?key={key}");
if ((int)resp.StatusCode == 403)
throw new InvalidOperationException("Google returned 403 on setup probe - enable billing/Translation API or fix the key."); Try / catch
try
{
return await googleV2.Translate(text, src, tgt, token);
}
catch (Exception ex) when (ex.InnerException is WebException we && we.Message.Contains("(403) Forbidden."))
{
throw new InvalidOperationException("Google returned 403 - billing/API not enabled or key restricted/invalid.", ex);
} Prevention
- Enable billing on the GCP project (most common 403 cause).
- Do not IP-restrict the key to a dynamic address.
- Always inspect WebException.Response for Google's structured error rather than only string-matching.
- Share the empty-message fix from error 50 - both branches use the same throw and the same pitfall applies.
When it happens
Trigger: A WebException escapes with Message containing '(403) Forbidden.'; root cause is identical to error 48 (billing disabled, API not enabled, key invalid/restricted).
Common situations: Billing disabled; Translation API not enabled; key IP/referrer restrictions blocking the caller; runtime surfacing 403 as WebException.
Related errors
- API key invalid (or perhaps API/billing is not enabled)?
- API key invalid (or perhaps billing is not enabled)?
- "Perhaps billing is not enabled (or API key is invalid)?"
- Forbidden! {resultContent}
- Free API quota exceeded?
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/a40c845f0d6dd10f.
Report an issue: GitHub.