SubtitleEdit/subtitleedit · error · InvalidOperationException

Ollama returned {(int)resp.StatusCode}: {json}

Error message

Ollama returned {(int)resp.StatusCode}: {json}

What it means

Thrown by OllamaOcrEngine when the Ollama HTTP `/api/chat` endpoint returns a non-success status code. The message includes the numeric status and the raw JSON body so the caller can see Ollama's own error text. This is an upstream/network failure from the local (or remote) Ollama server, not a seconv bug.

Source

Thrown at src/seconv/Core/OllamaOcrEngine.cs:64

        using var data = image.Encode(SKEncodedImageFormat.Png, 90);
        var pngBytes = data.ToArray();
        var base64 = Convert.ToBase64String(pngBytes);

        var prompt = $"Act as a precise OCR engine. Transcribe every line of text from this image exactly as it appears. The language is {_language}. Maintain the vertical order. Use a single '\\n' to separate each line. Do not skip any text. Output only the transcribed text";
        var body = "{ \"model\": \"" + Escape(_model) + "\", " +
                   "\"messages\": [ { \"role\": \"user\", \"content\": \"" + Escape(prompt) + "\", " +
                   "\"images\": [ \"" + base64 + "\" ] } ], " +
                   "\"stream\": false }";

        using var content = new StringContent(body, Encoding.UTF8);
        content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

        var resp = _httpClient.PostAsync(_url, content).GetAwaiter().GetResult();
        var bodyBytes = resp.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
        var json = Encoding.UTF8.GetString(bodyBytes).Trim();
        if (!resp.IsSuccessStatusCode)
        {
            throw new InvalidOperationException($"Ollama returned {(int)resp.StatusCode}: {json}");
        }

        var parser = new SeJsonParser();
        var contents = parser.GetAllTagsByNameAsStrings(json, "content");
        var text = string.Join(string.Empty, contents).Trim();
        text = text.Replace("\\n", Environment.NewLine).Replace("\\\"", "\"");
        return text.Trim();
    }

    private static string Escape(string s) =>
        s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r");

    private static SKBitmap PadToSquare(SKBitmap source)
    {
        var margin = (int)(Math.Max(source.Width, source.Height) * 0.2);
        var side = Math.Max(source.Width, source.Height) + margin;
        var info = new SKImageInfo(side, side, SKColorType.Rgba8888, SKAlphaType.Opaque);
        var square = new SKBitmap(info);

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Confirm Ollama is up: `curl <ollama-url>/api/tags`.
  2. Pull the requested model: `ollama pull <model>`.
  3. Verify `--ollama-url` includes the full base (default usually `http://localhost:11434`).
  4. Reduce image size or pick a smaller model if Ollama returns 500/OOM.
  5. Read the JSON body in the error message — it usually states the exact upstream cause.

Example fix

# before
seconv in.sup out.srt --ocr-engine ollama --ollama-model llama3.2-vision
#  -> 'Ollama returned 404: model not found'
# after
ollama pull llama3.2-vision
seconv in.sup out.srt --ocr-engine ollama --ollama-model llama3.2-vision
Defensive patterns

Strategy: retry

Validate before calling

using var ping = new HttpClient { Timeout = TimeSpan.FromSeconds(3) };
var probe = await ping.GetAsync(options.OllamaUrl.TrimEnd('/') + "/api/tags");
if (!probe.IsSuccessStatusCode)
    throw new InvalidOperationException("Ollama unreachable at " + options.OllamaUrl);

Type guard

static bool IsLikelyOllamaUrl(string? url) =>
    !string.IsNullOrWhiteSpace(url) && Uri.TryCreate(url, UriKind.Absolute, out _);

Try / catch

for (int attempt = 0; attempt < 3; attempt++)
{
    try { return engine.Recognize(bitmap); }
    catch (InvalidOperationException ex) when (ex.Message.Contains("Ollama returned") && attempt < 2)
    {
        await Task.Delay(TimeSpan.FromSeconds(1 << attempt));
        continue;
    }
}

Prevention

When it happens

Trigger: Calling `OllamaOcrEngine.Recognize` (via `--ocr-engine ollama`) when the POST to `options.OllamaUrl` returns e.g. 404 (model not pulled), 500 (Ollama crashed), 401 (auth on a proxy), or connection-level errors surfaced as non-2xx.

Common situations: Ollama not running (`--ollama-url` pointing at a dead port); the named model not pulled (`ollama pull <model>`); wrong URL base (forgot `/api/chat`); Ollama version too old to support the chat API; GPU/CPU OOM on a large image.

Related errors


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