{"record":{"id":"3f40beb1c5036ff8","repo":"SubtitleEdit/subtitleedit","slug":"vobsub-idx-input-has-no-companion-sub-path-3f40be","errorCode":null,"errorMessage":"VobSub '.idx' input has no companion '.sub' ({Path.GetFileName(subPath)}) — the .idx only holds timing and palette; the subtitle images live in the .sub.","messagePattern":"VobSub '\\.idx' input has no companion '\\.sub' \\((.+?)\\) — the \\.idx only holds timing and palette; the subtitle images live in the \\.sub\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/seconv/Core/SubtitleConverter.cs","lineNumber":330,"sourceCode":"                }\n\n                // IsPal: default to PAL to match VobSubExtractor. The .idx \"size:\" field\n                // could disambiguate per-file, but a wrong guess only affects timing scale,\n                // not bitmap content.\n                return await PassThroughSingleStreamAsync(inputFile, options, result, fileIndex,\n                    () => BitmapSubtitleLoader.LoadVobSub(inputFile, idxPath, isPal: true));\n            }\n            return false;\n        }\n\n        if (ext == \".idx\")\n        {\n            // Accept the .idx of a VobSub pair as input (5.0.0 did): redirect to the\n            // companion .sub and use this .idx for timing + palette (issue #12772).\n            var subPath = Path.ChangeExtension(inputFile, \".sub\");\n            if (!File.Exists(subPath))\n            {\n                throw new InvalidOperationException(\n                    $\"VobSub '.idx' input has no companion '.sub' ({Path.GetFileName(subPath)}) — \"\n                    + \"the .idx only holds timing and palette; the subtitle images live in the .sub.\");\n            }\n\n            return await PassThroughSingleStreamAsync(subPath, options, result, fileIndex,\n                () => BitmapSubtitleLoader.LoadVobSub(subPath, inputFile, isPal: true));\n        }\n\n        if (ext is \".mkv\" or \".mks\")\n        {\n            return await PassThroughMatroskaPgsAsync(inputFile, options, result, fileIndex);\n        }\n\n        if (ext is \".ts\" or \".m2ts\" or \".mts\")\n        {\n            return await PassThroughTransportStreamDvbAsync(inputFile, options, result, fileIndex);\n        }\n","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/seconv/Core/SubtitleConverter.cs#L312-L348","documentation":"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.","triggerScenarios":"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`.","commonSituations":"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.","solutions":["Place the matching `.sub` file next to the `.idx` (same base name, same directory).","Point seconv directly at the `.sub` file instead of the `.idx`.","Re-download or re-extract the VobSub pair so both halves are present.","If you only have the .idx and no .sub, the subtitle images are lost — re-extract from the source disc/mkv."],"exampleFix":"# before: only movie.idx present\nseconv movie.idx out.srt\n# after: also copy movie.sub next to it\nseconv movie.idx out.srt","handlingStrategy":"validation","validationCode":"if (Path.GetExtension(inputFile).Equals(\".idx\", StringComparison.OrdinalIgnoreCase))\n{\n    var sub = Path.ChangeExtension(inputFile, \".sub\");\n    if (!File.Exists(sub))\n        throw new ArgumentException(\"Missing companion .sub for \" + inputFile);\n}","typeGuard":"static bool VobSubPairIsComplete(string idxPath)\n{\n    if (!idxPath.EndsWith(\".idx\", StringComparison.OrdinalIgnoreCase)) return true;\n    return File.Exists(Path.ChangeExtension(idxPath, \".sub\"));\n}","tryCatchPattern":"try { await converter.ConvertAsync(options); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"no companion '.sub'\"))\n{\n    // locate the .sub, or ask user to provide the pair\n}","preventionTips":["Keep .idx and .sub together when copying VobSub assets.","Point seconv at the .sub directly to skip the .idx lookup.","Add a pre-flight check that both halves exist for .idx inputs."],"tags":["vobsub","idx","sub","file-not-found","input","container"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}