{"record":{"id":"eb7159a2e1a0a7ab","repo":"SubtitleEdit/subtitleedit","slug":"api-key-is-not-valid-jsonresult","errorCode":null,"errorMessage":"API key is not valid!\n\n {jsonResult}","messagePattern":"API key is not valid!\n\n (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":401,"severity":"error","filePath":"src/libuilogic/AutoTranslate/MicrosoftTranslator.cs","lineNumber":99,"sourceCode":"            var httpClient = GetTranslateClient();\n            var jsonBuilder = new StringBuilder();\n            jsonBuilder.Append(\"[\");\n            jsonBuilder.Append(\"{ \\\"Text\\\":\\\"\" + Json.EncodeJsonText(text) + \"\\\"}\");\n            jsonBuilder.Append(\"]\");\n            var json = jsonBuilder.ToString();\n            var content = new StringContent(json, Encoding.UTF8);\n            content.Headers.ContentType = MediaTypeHeaderValue.Parse(\"application/json\");\n            var result = await httpClient.PostAsync(url, content, cancellationToken);\n            var parser = new JsonParser();\n            var jsonResult = await result.Content.ReadAsStringAsync(cancellationToken);\n\n            if (!result.IsSuccessStatusCode)\n            {\n                Error = jsonResult;\n\n                if (result.StatusCode == HttpStatusCode.Unauthorized)\n                {\n                    throw new Exception(\"API key is not valid!\" + Environment.NewLine + Environment.NewLine + jsonResult);\n                }\n\n                throw new Exception(\"An error occurred during translate:\" + Environment.NewLine + Environment.NewLine + jsonResult);\n            }\n\n            var x = (List<object>)parser.Parse(jsonResult);\n            foreach (var xElement in x)\n            {\n                var dict = (Dictionary<string, object>)xElement;\n                var y = (List<object>)dict[\"translations\"];\n                foreach (var o in y)\n                {\n                    var textDictionary = (Dictionary<string, object>)o;\n                    var res = (string)textDictionary[\"text\"];\n                    res = res.Replace(\"<br />\", Environment.NewLine);\n                    res = res.Replace(\"<br/>\", Environment.NewLine);\n                    res = res.Replace(\"<br>\", Environment.NewLine);\n                    results.Add(res);","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/libuilogic/AutoTranslate/MicrosoftTranslator.cs#L81-L117","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nif (result.StatusCode == HttpStatusCode.Unauthorized)\n{\n    throw new Exception(\"API key is not valid!\" + Environment.NewLine + Environment.NewLine + jsonResult);\n}\n\n// after - attempt one transparent token refresh before failing, since 401 mid-run is usually a stale token\nif (result.StatusCode == HttpStatusCode.Unauthorized && !_refreshedTokenThisCall)\n{\n    _refreshedTokenThisCall = true;\n    _accessToken = GetAccessToken(_apiKey, _tokenEndpoint);\n    _accessTokenFetchedUtc = DateTime.UtcNow;\n    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", _accessToken);\n    // caller retries; or loop the post once here\n}\nthrow new Exception(\"Azure Translator API key/token is not valid!\" + Environment.NewLine + Environment.NewLine + jsonResult);","handlingStrategy":"retry","validationCode":"// Before a long batch, confirm the token is fresh and the resource accepts it\nif (DateTime.UtcNow - _accessTokenFetchedUtc > AccessTokenLifetime)\n{\n    _accessToken = GetAccessToken(_apiKey, _tokenEndpoint);\n    _accessTokenFetchedUtc = DateTime.UtcNow;\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    return await translator.Translate(text, src, tgt, token);\n}\ncatch (Exception ex) when (ex.Message.StartsWith(\"API key is not valid!\", StringComparison.Ordinal))\n{\n    // One transparent token refresh, then one retry\n    translator.ReinitializeToken();\n    return await translator.Translate(text, src, tgt, token);\n}","preventionTips":["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."],"tags":["api","auth","azure","translation","token-expiry"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}