{"record":{"id":"d5952adccb7aa5e7","repo":"SubtitleEdit/subtitleedit","slug":"yt-dlp-finished-but-no-video-file-was-produced-te","errorCode":null,"errorMessage":"yt-dlp finished but no video file was produced.\nTemp directory: {tempDir}\nContents: {files}","messagePattern":"yt-dlp finished but no video file was produced\\.\nTemp directory: (.+?)\nContents: (.+?)","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"error","filePath":"src/ui/Features/Video/OpenFromUrl/DownloadVideoFromUrlViewModel.cs","lineNumber":169,"sourceCode":"        // Pass yt-dlp the literal \"%(ext)s\" placeholder so it picks the actual\n        // container extension. If we use the user's chosen extension verbatim\n        // (e.g. \"download.mkv\"), yt-dlp treats the whole thing as the template\n        // stem and writes \"download.mkv.webm\" after the merge — leaving us\n        // unable to find the produced file by predicted name.\n        var templatePath = Path.Combine(tempDir, \"download.%(ext)s\");\n\n        try\n        {\n            await _ytDlpDownloadService.DownloadVideo(_url, templatePath, _downloadSubtitles, progress, cancellationToken, stageProgress);\n\n            // Everything from here on is silent too: moving the file out of the temp dir can\n            // be a full copy across volumes, and the auto-caption fetch is another yt-dlp run.\n            stageProgress.Report(YtDlpDownloadStage.PostProcessing);\n\n            var actualVideoPath = FindProducedVideo(tempDir);\n            if (actualVideoPath is null)\n            {\n                throw new FileNotFoundException(\n                    \"yt-dlp finished but no video file was produced.\" + Environment.NewLine +\n                    $\"Temp directory: {tempDir}\" + Environment.NewLine +\n                    \"Contents: \" + (Directory.Exists(tempDir)\n                        ? string.Join(\", \", Directory.EnumerateFiles(tempDir).Select(Path.GetFileName))\n                        : \"<missing>\"));\n            }\n\n            if (File.Exists(OutputPath))\n            {\n                File.Delete(OutputPath);\n            }\n            File.Move(actualVideoPath, OutputPath);\n\n            if (_downloadSubtitles)\n            {\n                if (_includeAutoGeneratedSubtitles)\n                {\n                    try","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Features/Video/OpenFromUrl/DownloadVideoFromUrlViewModel.cs#L151-L187","documentation":"Thrown when yt-dlp exits without error but FindProducedVideo cannot locate any valid video file in the temp directory. FindProducedVideo enumerates files matching 'download.*', excluding subtitle sidecars (names with two dots like 'download.en.srt') and incomplete '.part' files. If nothing remains, the download is treated as failed despite yt-dlp reporting success.","triggerScenarios":"yt-dlp writes the final file with a name that doesn't match 'download.*' (e.g. it used a different template stem); yt-dlp wrote only subtitle files and no video; the merge step failed silently leaving only .part files; the output was written to a different directory than tempDir; yt-dlp completed but the temp directory was concurrently cleaned.","commonSituations":"yt-dlp version upgrade changed default output naming behaviour; the URL is audio-only or subtitle-only with no video stream; yt-dlp's merge step (e.g. ffmpeg) is missing and it leaves fragmented .part files; the template path with %(ext)s was overridden by yt-dlp config; a concurrent cleanup process removed the temp dir.","solutions":["Check the temp directory contents listed in the error message to see what yt-dlp actually wrote.","Verify ffmpeg/ffprobe is installed and on PATH — yt-dlp needs it for merging separate audio/video streams.","Update yt-dlp to the latest version (yt-dlp self-update or reinstall).","Test the URL directly with yt-dlp from the command line using the same -o template to see what filename it produces.","If the URL is audio-only, use an audio download flow instead of video.","Check yt-dlp's --verbose output for merge failures or post-processing errors."],"exampleFix":"// before\nvar actualVideoPath = FindProducedVideo(tempDir);\nif (actualVideoPath is null)\n{\n    throw new FileNotFoundException(\n        \"yt-dlp finished but no video file was produced.\" + Environment.NewLine +\n        $\"Temp directory: {tempDir}\" + Environment.NewLine +\n        \"Contents: \" + (Directory.Exists(tempDir)\n            ? string.Join(\", \", Directory.EnumerateFiles(tempDir).Select(Path.GetFileName))\n            : \"<missing>\"));\n}\n\n// after — broaden the search and include yt-dlp's own output in the message\nvar actualVideoPath = FindProducedVideo(tempDir);\nif (actualVideoPath is null)\n{\n    // Fall back: accept any non-subtitle, non-part file in the temp dir\n    var allFiles = Directory.Exists(tempDir)\n        ? Directory.EnumerateFiles(tempDir).ToList()\n        : new List<string>();\n    actualVideoPath = allFiles.FirstOrDefault(f =>\n    {\n        var ext = Path.GetExtension(f).ToLowerInvariant();\n        return !new[] { \".srt\", \".vtt\", \".ass\", \".ssa\", \".sub\", \".part\" }.Contains(ext);\n    });\n}\nif (actualVideoPath is null)\n{\n    throw new FileNotFoundException(\n        \"yt-dlp finished but no video file was produced.\" + Environment.NewLine +\n        $\"Temp directory: {tempDir}\" + Environment.NewLine +\n        \"Contents: \" + (Directory.Exists(tempDir)\n            ? string.Join(\", \", Directory.EnumerateFiles(tempDir).Select(Path.GetFileName))\n            : \"<missing>\"));\n}","handlingStrategy":"try-catch","validationCode":"// Pre-validate the URL with yt-dlp's --simulate flag\nvar psi = new ProcessStartInfo(\"yt-dlp\", $\"--simulate --no-warnings \\\"{_url}\\\"\")\n{ RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false };\nusing var proc = Process.Start(psi);\nawait proc.WaitForExitAsync();\nif (proc.ExitCode != 0)\n    throw new InvalidOperationException(\"yt-dlp cannot resolve URL: \" + _url);","typeGuard":null,"tryCatchPattern":"try { await _ytDlpDownloadService.DownloadVideo(...); }\ncatch (FileNotFoundException ex) when (ex.Message.Contains(\"no video file was produced\"))\n{ /* read temp dir contents from message, check for .part files (incomplete), suggest ffmpeg install or retry */ }","preventionTips":["Ensure ffmpeg and ffprobe are installed and on PATH — yt-dlp requires them for merging audio/video streams.","Keep yt-dlp updated to the latest version.","Test URLs with yt-dlp CLI before relying on the programmatic wrapper.","Avoid URLs that are known to be audio-only or stream-only without downloadable video."],"tags":["network","yt-dlp","video-download","file-not-found"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}