SubtitleEdit/subtitleedit · error · InvalidOperationException

VobSub '.idx' input has no companion '.sub' ({Path.GetFileNa

Error message

VobSub '.idx' input has no companion '.sub' ({Path.GetFileName(subPath)}) — the .idx only holds timing and palette; the subtitle images live in the .sub.

What it means

Thrown when a `.idx` file is given as input but no sibling `.sub` file exists alongside it. VobSub splits a subtitle across two files: the .idx holds timing and palette metadata, while the .sub holds the actual MPEG-2 subpicture image packets. seconv accepts the .idx as an entry point but must read the .sub for the images, so a missing .sub makes conversion impossible.

Source

Thrown at src/seconv/Core/SubtitleConverter.cs:330

                }

                // IsPal: default to PAL to match VobSubExtractor. The .idx "size:" field
                // could disambiguate per-file, but a wrong guess only affects timing scale,
                // not bitmap content.
                return await PassThroughSingleStreamAsync(inputFile, options, result, fileIndex,
                    () => BitmapSubtitleLoader.LoadVobSub(inputFile, idxPath, isPal: true));
            }
            return false;
        }

        if (ext == ".idx")
        {
            // Accept the .idx of a VobSub pair as input (5.0.0 did): redirect to the
            // companion .sub and use this .idx for timing + palette (issue #12772).
            var subPath = Path.ChangeExtension(inputFile, ".sub");
            if (!File.Exists(subPath))
            {
                throw new InvalidOperationException(
                    $"VobSub '.idx' input has no companion '.sub' ({Path.GetFileName(subPath)}) — "
                    + "the .idx only holds timing and palette; the subtitle images live in the .sub.");
            }

            return await PassThroughSingleStreamAsync(subPath, options, result, fileIndex,
                () => BitmapSubtitleLoader.LoadVobSub(subPath, inputFile, isPal: true));
        }

        if (ext is ".mkv" or ".mks")
        {
            return await PassThroughMatroskaPgsAsync(inputFile, options, result, fileIndex);
        }

        if (ext is ".ts" or ".m2ts" or ".mts")
        {
            return await PassThroughTransportStreamDvbAsync(inputFile, options, result, fileIndex);
        }

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Place the matching `.sub` file next to the `.idx` (same base name, same directory).
  2. Point seconv directly at the `.sub` file instead of the `.idx`.
  3. Re-download or re-extract the VobSub pair so both halves are present.
  4. If you only have the .idx and no .sub, the subtitle images are lost — re-extract from the source disc/mkv.

Example fix

# before: only movie.idx present
seconv movie.idx out.srt
# after: also copy movie.sub next to it
seconv movie.idx out.srt
Defensive patterns

Strategy: validation

Validate before calling

if (Path.GetExtension(inputFile).Equals(".idx", StringComparison.OrdinalIgnoreCase))
{
    var sub = Path.ChangeExtension(inputFile, ".sub");
    if (!File.Exists(sub))
        throw new ArgumentException("Missing companion .sub for " + inputFile);
}

Type guard

static bool VobSubPairIsComplete(string idxPath)
{
    if (!idxPath.EndsWith(".idx", StringComparison.OrdinalIgnoreCase)) return true;
    return File.Exists(Path.ChangeExtension(idxPath, ".sub"));
}

Try / catch

try { await converter.ConvertAsync(options); }
catch (InvalidOperationException ex) when (ex.Message.Contains("no companion '.sub'"))
{
    // locate the .sub, or ask user to provide the pair
}

Prevention

When it happens

Trigger: Calling ConvertAsync with an input whose extension is `.idx` and for which `Path.ChangeExtension(inputFile, '.sub')` does not exist on disk. Typically `movie.idx` was copied without `movie.sub`.

Common situations: Copying only the .idx half of a VobSub pair; renaming or moving the .sub independently; a download site shipping only the .idx; the .sub present under a different base name than the .idx.

Related errors


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