SubtitleEdit/subtitleedit · error · Exception

API key invalid (or perhaps API/billing is not enabled)?

Error message

API key invalid (or perhaps API/billing is not enabled)?

What it means

Thrown inside GoogleTranslateV2's catch (WebException) block when the exception message contains '(400) Bad Request'. This path is hit when the underlying transport (WebRequest-era stack) wraps a 400 response as a WebException rather than surfacing it as an HttpResponseMessage. The message maps 400 to an API-key/billing diagnosis. Note: if the WebException message matches neither branch, 'message' stays empty and the throw loses text (though the inner exception is preserved).

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

  1. Same as error 47: verify the API key, enable the Translation API, and enable billing.
  2. Inspect the inner WebException Response for Google's JSON error detail.
  3. Set GoogleApiV2Key before calling Initialize/Translate.

Example fix

// before
if (webException.Message.Contains("(400) Bad Request"))
{
    message = "API key invalid (or perhaps API/billing is not enabled)?";
}
...
throw new Exception(message, webException);

// after - guarantee a non-empty message even when neither branch matches
var message = $"GoogleTranslateV2 transport error: {webException.Status}";
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);
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate key presence before calling (the 400 transport path is just another symptom of a bad key)
var key = Configuration.Settings.Tools.GoogleApiV2Key;
if (string.IsNullOrWhiteSpace(key))
    throw new InvalidOperationException("Google V2 API key is not set - a 400 will follow.");

Try / catch

try
{
    return await googleV2.Translate(text, src, tgt, token);
}
catch (Exception ex) when (ex.InnerException is WebException we && we.Message.Contains("(400) Bad Request"))
{
    throw new InvalidOperationException("Google rejected the request (400) - invalid key or API/billing not enabled.", ex);
}
catch (Exception ex) when (string.IsNullOrWhiteSpace(ex.Message) && ex.InnerException is WebException)
{
    // Guard against the empty-message pitfall: always surface something
    throw new InvalidOperationException($"GoogleTranslateV2 transport error: {((WebException)ex.InnerException).Status}", ex);
}

Prevention

When it happens

Trigger: A WebException escapes the try at GoogleTranslateV2.cs:56; its Message contains '(400) Bad Request'. Same root cause as error 47 (invalid key / billing / API not enabled) but reached via the legacy exception path.

Common situations: Running on a runtime/config where the 400 is thrown as WebException instead of being delivered as a response; empty key; key for a project without the Translation API or billing enabled.

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/27c7652a067d4d88. Report an issue: GitHub.