SubtitleEdit/subtitleedit · error · Exception

API key is not valid! {jsonResult}

Error message

API key is not valid!

 {jsonResult}

What it means

Thrown by MicrosoftTranslator.Translate() when Azure Cognitive Services Translator returns HTTP 401 Unauthorized. A 401 during translate means the bearer access token is invalid, expired, or for a different resource/region than the translate endpoint (api.cognitive.microsofttranslator.com).

Source

Thrown at src/libuilogic/AutoTranslate/MicrosoftTranslator.cs:99

            var httpClient = GetTranslateClient();
            var jsonBuilder = new StringBuilder();
            jsonBuilder.Append("[");
            jsonBuilder.Append("{ \"Text\":\"" + Json.EncodeJsonText(text) + "\"}");
            jsonBuilder.Append("]");
            var json = jsonBuilder.ToString();
            var content = new StringContent(json, Encoding.UTF8);
            content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
            var result = await httpClient.PostAsync(url, content, cancellationToken);
            var parser = new JsonParser();
            var jsonResult = await result.Content.ReadAsStringAsync(cancellationToken);

            if (!result.IsSuccessStatusCode)
            {
                Error = jsonResult;

                if (result.StatusCode == HttpStatusCode.Unauthorized)
                {
                    throw new Exception("API key is not valid!" + Environment.NewLine + Environment.NewLine + jsonResult);
                }

                throw new Exception("An error occurred during translate:" + Environment.NewLine + Environment.NewLine + jsonResult);
            }

            var x = (List<object>)parser.Parse(jsonResult);
            foreach (var xElement in x)
            {
                var dict = (Dictionary<string, object>)xElement;
                var y = (List<object>)dict["translations"];
                foreach (var o in y)
                {
                    var textDictionary = (Dictionary<string, object>)o;
                    var res = (string)textDictionary["text"];
                    res = res.Replace("<br />", Environment.NewLine);
                    res = res.Replace("<br/>", Environment.NewLine);
                    res = res.Replace("<br>", Environment.NewLine);
                    results.Add(res);

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Re-run Initialize() to mint a fresh token (or restart the translation).
  2. Verify the key and token endpoint region match the translate resource region.
  3. Regenerate the key in Azure and update MicrosoftTranslatorApiKey.
  4. Confirm the Azure Translator resource is still active (not deleted/paused).

Example fix

// before
if (result.StatusCode == HttpStatusCode.Unauthorized)
{
    throw new Exception("API key is not valid!" + Environment.NewLine + Environment.NewLine + jsonResult);
}

// after - attempt one transparent token refresh before failing, since 401 mid-run is usually a stale token
if (result.StatusCode == HttpStatusCode.Unauthorized && !_refreshedTokenThisCall)
{
    _refreshedTokenThisCall = true;
    _accessToken = GetAccessToken(_apiKey, _tokenEndpoint);
    _accessTokenFetchedUtc = DateTime.UtcNow;
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
    // caller retries; or loop the post once here
}
throw new Exception("Azure Translator API key/token is not valid!" + Environment.NewLine + Environment.NewLine + jsonResult);
Defensive patterns

Strategy: retry

Validate before calling

// Before a long batch, confirm the token is fresh and the resource accepts it
if (DateTime.UtcNow - _accessTokenFetchedUtc > AccessTokenLifetime)
{
    _accessToken = GetAccessToken(_apiKey, _tokenEndpoint);
    _accessTokenFetchedUtc = DateTime.UtcNow;
}

Try / catch

try
{
    return await translator.Translate(text, src, tgt, token);
}
catch (Exception ex) when (ex.Message.StartsWith("API key is not valid!", StringComparison.Ordinal))
{
    // One transparent token refresh, then one retry
    translator.ReinitializeToken();
    return await translator.Translate(text, src, tgt, token);
}

Prevention

When it happens

Trigger: Translate() posts the text to translate?api-version=3.0&from=...&to=... with an Authorization: Bearer <token> header; Azure returns 401. Can happen mid-run if the token passed its 8-minute refresh window (AccessTokenLifetime) and the refresh logic at GetTranslateClient did not fire, or if the key/region mismatch invalidates even a freshly minted token.

Common situations: Key region does not match the global translate endpoint; key rotated in Azure but not updated in SubtitleEdit settings; token endpoint and translate endpoint in different regions; subscription disabled.

Related errors


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