SubtitleEdit/subtitleedit · error · InvalidOperationException

No vision projector found next to {fullPath}. llama.cpp OCR

Error message

No vision projector found next to {fullPath}. llama.cpp OCR models need their mmproj sidecar; expected 'mmproj-{fileName}' or '{stem}-mmproj.gguf' in the same folder.

What it means

Thrown when a full .gguf model path exists (File.Exists passes) but no mmproj vision-projector sidecar file is found next to it. llama.cpp vision/OCR models need both the main model and a projector file; without the projector the model cannot process images at all. The engine searches for two naming conventions: 'mmproj-<filename>' (e.g. mmproj-model.gguf) and '<stem>-mmproj.gguf' (e.g. model-mmproj.gguf) in the same directory.

Source

Thrown at src/seconv/Core/LlamaCppOcrEngine.cs:156

        {
            var name = requestedModel.Trim();

            // Full path to a .gguf: use it directly, but require the mmproj sidecar - a vision
            // model served without its projector can't see the image at all.
            if (Path.IsPathRooted(name) || name.Contains(Path.DirectorySeparatorChar) || name.Contains(Path.AltDirectorySeparatorChar))
            {
                if (!File.Exists(name))
                {
                    throw new InvalidOperationException($"OCR model file not found: {name}");
                }

                var fullPath = Path.GetFullPath(name);
                var mmproj = FindMmprojSidecar(fullPath);
                if (mmproj == null)
                {
                    var fileName = Path.GetFileName(fullPath);
                    var stem = Path.GetFileNameWithoutExtension(fullPath);
                    throw new InvalidOperationException(
                        $"No vision projector found next to {fullPath}. llama.cpp OCR models need their mmproj sidecar; " +
                        $"expected 'mmproj-{fileName}' or '{stem}-mmproj.gguf' in the same folder.");
                }

                return new LlamaCppModel(Path.GetFileName(fullPath), fullPath, string.Empty, Url: string.Empty,
                    MmprojFileName: mmproj);
            }

            // Name: match the curated OCR models (with or without .gguf).
            var model = LlamaCppServerManager.OcrModels.FirstOrDefault(m => m.FileName.Equals(name, StringComparison.OrdinalIgnoreCase))
                        ?? LlamaCppServerManager.OcrModels.FirstOrDefault(m => m.FileName.Equals(name + ".gguf", StringComparison.OrdinalIgnoreCase))
                        ?? LlamaCppServerManager.OcrModels.FirstOrDefault(m => m.DisplayName.Equals(name, StringComparison.OrdinalIgnoreCase));
            if (model == null || !LlamaCppServerManager.IsModelInstalled(model))
            {
                throw new InvalidOperationException(
                    $"OCR model '{name}' not found in {LlamaCppServerManager.GetAndCreateModelsFolder()}. " +
                    "Download one in Subtitle Edit (OCR window > llama.cpp) or pass a full path via --ocr-model. " +
                    $"Curated models: {curatedNames}.");

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Download the mmproj sidecar from the same model page and place it next to the main .gguf, named either 'mmproj-<filename>' or '<stem>-mmproj.gguf'.
  2. If the sidecar has a different name, rename it to match one of the two expected conventions.
  3. Verify both files exist in the same directory with a directory listing before running OCR.
Defensive patterns

Strategy: validation

Validate before calling

static string? FindMmproj(string modelPath)
{
    var dir = Path.GetDirectoryName(modelPath)!;
    var fileName = Path.GetFileName(modelPath);
    var stem = Path.GetFileNameWithoutExtension(modelPath);
    return new[] { $"mmproj-{fileName}", $"{stem}-mmproj.gguf" }
        .Select(n => Path.Combine(dir, n))
        .FirstOrDefault(File.Exists);
}
// Before ResolveModel:
if (FindMmproj(fullPath) == null)
    Console.Error.WriteLine("Missing mmproj sidecar — download and place it next to the model.");

Type guard

static bool HasMmprojSidecar(string modelPath) => FindMmproj(modelPath) != null;

Try / catch

try { var model = LlamaCppOcrEngine.ResolveModel(modelArg); }
catch (InvalidOperationException ex) when (ex.Message.Contains("No vision projector found"))
{ /* download mmproj sidecar and place next to model */ }

Prevention

When it happens

Trigger: Downloading only the main .gguf model without its mmproj sidecar; the sidecar has an unexpected name that matches neither convention; the sidecar is in a different directory than the model.

Common situations: Downloading a vision model from HuggingFace and missing the separate mmproj file download link; renaming the model file without renaming the sidecar; the model download was incomplete.

Related errors


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