{"record":{"id":"4a8ca1365736fee0","repo":"Tyrrrz/YoutubeDownloader","slug":"no-suitable-download-option-found","errorCode":null,"errorMessage":"No suitable download option found.","messagePattern":"No suitable download option found\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"YoutubeDownloader.Core/Downloading/VideoDownloader.cs","lineNumber":44,"sourceCode":"        var manifest = await _youtube.Videos.Streams.GetManifestAsync(videoId, cancellationToken);\n        return VideoDownloadOption.ResolveAll(manifest, includeLanguageSpecificAudioStreams);\n    }\n\n    public async Task<VideoDownloadOption> GetBestDownloadOptionAsync(\n        VideoId videoId,\n        VideoDownloadPreference preference,\n        bool includeLanguageSpecificAudioStreams = true,\n        CancellationToken cancellationToken = default\n    )\n    {\n        var options = await GetDownloadOptionsAsync(\n            videoId,\n            includeLanguageSpecificAudioStreams,\n            cancellationToken\n        );\n\n        return preference.TryGetBestOption(options)\n            ?? throw new InvalidOperationException(\"No suitable download option found.\");\n    }\n\n    public async Task DownloadVideoAsync(\n        string filePath,\n        IVideo video,\n        VideoDownloadOption downloadOption,\n        bool includeSubtitles = true,\n        string? ffmpegPath = null,\n        IProgress<Percentage>? progress = null,\n        CancellationToken cancellationToken = default\n    )\n    {\n        // Include subtitles in the output container\n        var trackInfos = new List<ClosedCaptionTrackInfo>();\n        if (includeSubtitles && !downloadOption.Container.IsAudioOnly)\n        {\n            var manifest = await _youtube.Videos.ClosedCaptions.GetManifestAsync(\n                video.Id,","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/Tyrrrz/YoutubeDownloader/blob/bbcff039515a2ac985e3769f0243a58645a52b28/YoutubeDownloader.Core/Downloading/VideoDownloader.cs#L26-L62","documentation":"Thrown by VideoDownloader.GetBestDownloadOptionAsync when preference.TryGetBestOption(options) returns null - none of the resolved stream options satisfy the user's VideoDownloadPreference (container + video-quality preference). The options come from VideoDownloadOption.ResolveAll(manifest, ...) over the stream manifest fetched from YouTube via YoutubeExplode.","triggerScenarios":"The manifest contains no combination matching the preference: preference demands MP4 but only WebM/adaptive streams exist; the requested quality tier isn't published; the video is audio-only, a live stream, age-restricted, membership/premium-only, region-blocked, or removed; or includeLanguageSpecificAudioStreams=false filtered out the only audio track needed to pair with a video stream.","commonSituations":"A restrictive container/quality preference; YouTube rotating codecs so the preferred container disappears for a video; missing cookies for login/age-gated content; language-specific audio excluded leaving nothing muxable; access-restricted or transiently unavailable streams.","solutions":["Broaden the VideoDownloadPreference (allow MP4 and WebM, multiple quality tiers) so TryGetBestOption has candidates.","Call GetDownloadOptionsAsync first and choose from the returned list, or iterate progressively relaxed preferences until one yields an option.","Re-fetch the manifest (availability is transient) and verify access - authenticate with cookies for age/login-gated videos before retrying."],"exampleFix":"// before: throws when nothing matches the strict preference\nvar option = await downloader.GetBestDownloadOptionAsync(videoId, preference, ct);\n\n// after: inspect available options and pick a safe fallback\nvar options = await downloader.GetDownloadOptionsAsync(videoId, includeLanguageSpecificAudioStreams: true, ct);\nvar option = preference.TryGetBestOption(options)\n    ?? options.FirstOrDefault()\n    ?? throw new InvalidOperationException(\"Video has no downloadable streams.\");","handlingStrategy":"validation","validationCode":"var options = await downloader.GetDownloadOptionsAsync(\n    videoId, includeLanguageSpecificAudioStreams: true, ct);\n\nif (options.Count is 0)\n{\n    // tell the user the video has no downloadable streams (private/removed/live/restricted)\n}\n\nvar chosen = preference.TryGetBestOption(options);\nif (chosen is null)\n{\n    // surface the actually-available containers/qualities and relax the preference\n}","typeGuard":null,"tryCatchPattern":"VideoDownloadOption option;\ntry\n{\n    option = await downloader.GetBestDownloadOptionAsync(videoId, preference, cancellationToken: ct);\n}\ncatch (InvalidOperationException ex) when (ex.Message == \"No suitable download option found.\")\n{\n    var fallback = await downloader.GetDownloadOptionsAsync(videoId, cancellationToken: ct);\n    option = fallback.FirstOrDefault() ?? throw;\n}","preventionTips":["Surface the available containers/qualities to the user so their preference is informed.","Default to permissive preferences and include language-specific audio streams.","Resolve access restrictions (cookies/login) before offering the download."],"tags":["youtube","streams","manifest","preference","video-download","youtubeexplode"],"backgroundTag":null,"analyzedSha":"bbcff039515a2ac985e3769f0243a58645a52b28","analyzedAt":"2026-08-13T14:28:37.801Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}