SubtitleEdit/subtitleedit · error · Exception
{StaticName} returned an unexpected response: {responseStrin
Error message
{StaticName} returned an unexpected response: {responseString} What it means
Thrown by NoLanguageLeftBehindApi when the self-hosted NLLB service returns a 2xx response (EnsureSuccessStatusCode passed) but the JSON has no top-level 'result' field that the parser can extract. Indicates the service responded successfully but with an unexpected/empty schema, or the wrong service is at the configured URL.
Source
Thrown at src/libuilogic/AutoTranslate/NoLanguageLeftBehindApi.cs:67
{
return new NoLanguageLeftBehindServe().GetSupportedTargetLanguages();
}
public async Task<string> Translate(string text, string sourceLanguageCode, string targetLanguageCode, CancellationToken cancellationToken)
{
var content = new StringContent("{ \"text\": \"" + Json.EncodeJsonText(text) + "\", \"source\": \"" + sourceLanguageCode + "\", \"target\": \"" + targetLanguageCode + "\" }", Encoding.UTF8, "application/json");
using var result = await _httpClient.PostAsync("translator", content, cancellationToken);
result.EnsureSuccessStatusCode();
var responseString = await result.Content.ReadAsStringAsync(cancellationToken);
var parser = new SeJsonParser();
var resultText = parser.GetFirstObject(responseString, "result");
if (resultText == null)
{
Error = responseString;
SeLogger.Error($"{GetType().Name} got unexpected JSON: {responseString}");
throw new Exception($"{StaticName} returned an unexpected response: {responseString}");
}
return Json.DecodeJsonText(resultText);
}
public void Dispose() => _httpClient?.Dispose();
}
}
View on GitHub (pinned to 17a9f07487)
Solutions
- Confirm AutoTranslateNllbApiUrl points at the winstxnhdw/nllb-api implementation (returns {"result": ...}).
- Log/capture responseString to see the actual body and find the right key.
- Ensure the URL has a trailing slash (the code adds one, but verify the stored value).
- If using a different NLLB fork, switch to the matching translator class (e.g. NoLanguageLeftBehindServe).
Example fix
// before
var resultText = parser.GetFirstObject(responseString, "result");
if (resultText == null)
{
Error = responseString;
SeLogger.Error($"{GetType().Name} got unexpected JSON: {responseString}");
throw new Exception($"{StaticName} returned an unexpected response: {responseString}");
}
// after - try common alternative keys before giving up, and hint at a mismatched service
var resultText = parser.GetFirstObject(responseString, "result")
?? parser.GetFirstObject(responseString, "translatedText")
?? parser.GetFirstObject(responseString, "translation");
if (resultText == null)
{
Error = responseString;
SeLogger.Error($"{GetType().Name} got unexpected JSON (expected a 'result' key). URL: {_httpClient.BaseAddress}. Body: {responseString}");
throw new Exception($"{StaticName} returned an unexpected response (no 'result' key - is this the winstxnhdw/nllb-api service?): {responseString}");
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the NLLB URL and probe the service shape before translating
var url = Configuration.Settings.Tools.AutoTranslateNllbApiUrl;
if (string.IsNullOrWhiteSpace(url))
throw new InvalidOperationException("NLLB API URL is not set.");
using var c = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
var body = "{\"text\":\"hi\",\"source\":\"eng_Latn\",\"target\":\"spa_Latn\"}";
var resp = await c.PostAsync(url.TrimEnd('/') + "/translator", new StringContent(body, Encoding.UTF8, "application/json"));
var json = await resp.Content.ReadAsStringAsync();
if (!json.Contains("\"result\""))
throw new InvalidOperationException($"NLLB service at {url} did not return a 'result' key - is this winstxnhdw/nllb-api? Body: {json}"); Type guard
public static bool ResponseHasResultKey(string body) => body.Contains("\"result\""); Try / catch
try
{
return await nllb.Translate(text, src, tgt, token);
}
catch (Exception ex) when (ex.Message.Contains("unexpected response"))
{
throw new InvalidOperationException("NLLB returned a 2xx with no 'result' key - check the URL points at winstxnhdw/nllb-api and the schema matches.", ex);
} Prevention
- Ensure the URL points at the winstxnhdw/nllb-api fork (returns {"result": ...}).
- Keep a trailing slash on the base URL (the code adds one, but verify).
- Probe the service with a tiny request on setup to confirm the response shape.
- If using a different NLLB variant, use the matching translator class.
When it happens
Trigger: Translate() posts {text, source, target} to <base>/translator; result.EnsureSuccessStatusCode() passes; SeJsonParser.GetFirstObject(responseString, "result") returns null. Happens when the URL points to a different NLLB variant (e.g. nllb-serve vs winstxnhdw-nllb-api which returns {"result": ...}), or when the service returns an error-shaped 200 like {"error": ...}.
Common situations: AutoTranslateNllbApiUrl pointing at a different NLLB implementation whose response key is not 'result'; service returning {"translatedText": ...} or an error object with HTTP 200; missing trailing slash on the URL (the code appends one at Initialize to avoid a .NET base-address 404, but a wrong path still misroutes).
Related errors
- DeepLXTranslate error: StatusCode={result.StatusCode} {resul
- DeepLXTranslate gave empty alternatives: StatusCode={result.
- CrispASR exited with code {process.ExitCode}: {Error}
- 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/a9183d4f2d677474.
Report an issue: GitHub.