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
- Re-run Initialize() to mint a fresh token (or restart the translation).
- Verify the key and token endpoint region match the translate resource region.
- Regenerate the key in Azure and update MicrosoftTranslatorApiKey.
- 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
- Refresh the token well within its lifetime (the code uses an 8-min margin for a 10-min token - keep it).
- Match key region to the translate endpoint region to avoid 401 even with a fresh token.
- Rotate keys in Azure and update settings promptly to avoid revoked-key 401s.
- On a 401 mid-run, attempt a single re-initialize before surfacing the error.
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
- Can't get Access Token
- Could not get access token via {tokenEndpoint}: {result}
- Forbidden! {resultContent}
- API key invalid (or perhaps billing is not enabled)?
- "Perhaps billing is not enabled (or API key is invalid)?"
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/eb7159a2e1a0a7ab.
Report an issue: GitHub.