nilaoda/N_m3u8DL-RE · error · FileNotFoundException

ResString.mkvmergeNotFound

Error message

ResString.mkvmergeNotFound

What it means

When --use-mkvmerge is combined with --mux-after-done, DoWorkAsync requires the mkvmerge binary. It falls back to GlobalUtil.FindExecutable("mkvmerge") and throws FileNotFoundException with the localized resource string if mkvmerge cannot be located on disk.

Solutions

  1. Install MKVToolNix so mkvmerge is on PATH
  2. Pass --mkvmerge-binary-path with the full path to mkvmerge
  3. Verify with: mkvmerge --version
  4. Drop --use-mkvmerge to use the default ffmpeg muxer instead

Example fix

// before
N_m3u8DL-RE "URL" -M --use-mkvmerge
// after
N_m3u8DL-RE "URL" -M --use-mkvmerge --mkvmerge-binary-path "/usr/bin/mkvmerge"
Defensive patterns

Strategy: validation

Validate before calling

if (useMkvmerge && muxAfterDone)
{
    var mkv = GlobalUtil.FindExecutable("mkvmerge");
    if (mkv == null || !File.Exists(mkv)) throw new FileNotFoundException("install MKVToolNix or pass --mkvmerge-binary-path");
}

Type guard

static bool MkvmergeAvailable(CLIOptions o) => !o.MuxOptions.UseMkvmerge || (o.MkvmergeBinaryPath != null && File.Exists(o.MkvmergeBinaryPath)) || GlobalUtil.FindExecutable("mkvmerge") != null;

Try / catch

try { await DoWorkAsync(option); }
catch (FileNotFoundException ex) when (ex.Message.Contains("mkvmerge"))
{
    Console.Error.WriteLine("mkvmerge not found: install MKVToolNix or set --mkvmerge-binary-path");
}

Prevention

When it happens

Trigger: Running with --mux-after-done --use-mkvmerge while mkvmerge (from MKVToolNix) is not installed, not on PATH, or --mkvmerge-binary-path points to a missing file.

Common situations: Users choosing mkvmerge muxing without MKVToolNix installed; Windows PATH not including MKVToolNix install dir; typo in custom binary path.

Related errors


AI-assisted analysis of nilaoda/N_m3u8DL-RE@e113dee70c (2026-09-13). Data as JSON: /api/errors/9fa225a1c2b611a7. Report an issue: GitHub.

Appendix: source

Thrown at src/N_m3u8DL-RE/Program.cs:146

        }

        // 预先检查ffmpeg
        option.FFmpegBinaryPath ??= GlobalUtil.FindExecutable("ffmpeg");

        if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
        {
            throw new FileNotFoundException(ResString.ffmpegNotFound);
        }

        Logger.Extra($"ffmpeg => {option.FFmpegBinaryPath}");

        // 预先检查mkvmerge
        if (option is { MuxOptions.UseMkvmerge: true, MuxAfterDone: true })
        {
            option.MkvmergeBinaryPath ??= GlobalUtil.FindExecutable("mkvmerge");
            if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
            {
                throw new FileNotFoundException(ResString.mkvmergeNotFound);
            }
            Logger.Extra($"mkvmerge => {option.MkvmergeBinaryPath}");
        }

        // 预先检查
        if (option.Keys is { Length: > 0 } || option.KeyTextFile != null)
        {
            if (!string.IsNullOrEmpty(option.DecryptionBinaryPath) && !File.Exists(option.DecryptionBinaryPath))
            {
                throw new FileNotFoundException(option.DecryptionBinaryPath);
            }
            switch (option.DecryptionEngine)
            {
                case DecryptEngine.SHAKA_PACKAGER:
                {
                    var file = FindShakaPackager();
                    if (file == null) throw new FileNotFoundException(ResString.shakaPackagerNotFound);
                    option.DecryptionBinaryPath = file;

View on GitHub (pinned to e113dee70c)