nilaoda/N_m3u8DL-RE · error · Exception

A TRUN box should have a valid flags value

Error message

A TRUN box should have a valid flags value

What it means

In ExtractSub, the 'trun' box's flags field must be a valid value; 1000 is the sentinel MP4Parser assigns when the flags could not be read (truncated/malformed box). The library throws before calling ParseTRUN, because flags determine which optional per-sample fields follow and an invalid value would cause misparsing.

Solutions

  1. Re-download the segment and verify integrity (size or checksum).
  2. Validate the segment parses as complete MP4 boxes before feeding it to ExtractSub.
  3. Ensure the downloader honors Content-Length and does not write partial files.
  4. Use an MP4 box dumper to confirm the trun box flags (e.g. 0x000001/0x000100/0x000201).
Defensive patterns

Strategy: validation

Validate before calling

// ensure the segment is complete before extraction
if (dataSeg == null || dataSeg.Length < 8) throw new ArgumentException("segment too small");
// confirm trun flags are a known value via a box scan before ExtractSub

Type guard

bool HasValidTrunFlags(byte[] seg) => Mp4BoxScanner.Find(seg, "trun") is { } b && b.Flags is 0x000001 or 0x000100 or 0x000201 or 0x000204;

Try / catch

try { var subs = MP4VttUtil.ExtractSub(dataSeg, baseTime); }
catch (Exception ex) when (ex.Message.Contains("valid flags value")) {
    logger.Warn("Malformed trun flags; re-fetching segment");
    dataSeg = await reDownloadAsync(segmentUrl);
}

Prevention

When it happens

Trigger: ExtractSub is called on a segment whose 'trun' box is cut short so the 3-byte flags field cannot be read, yielding the sentinel value 1000.

Common situations: Truncated downloads over unstable connections; CDN edge nodes serving partial segments; concatenating or hex-editing segment data incorrectly.

Related errors


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

Appendix: source

Thrown at src/N_m3u8DL-RE.Parser/Mp4/MP4VttUtil.cs:79

                {
                    sawTFDT = true;
                    if (box.Version is not (0 or 1))
                        throw new Exception("TFDT version can only be 0 or 1");
                    baseTime = MP4Parser.ParseTFDT(box.Reader, box.Version);
                })
                .FullBox("tfhd", box =>
                {
                    if (box.Flags == 1000)
                        throw new Exception("A TFHD box should have a valid flags value");
                    defaultDuration = MP4Parser.ParseTFHD(box.Reader, box.Flags).DefaultSampleDuration;
                })
                .FullBox("trun", box =>
                {
                    sawTRUN = true;
                    if (box.Version == 1000)
                        throw new Exception("A TRUN box should have a valid version value");
                    if (box.Flags == 1000)
                        throw new Exception("A TRUN box should have a valid flags value");
                    presentations = MP4Parser.ParseTRUN(box.Reader, box.Version, box.Flags).SampleData;
                })
                .Box("mdat", MP4Parser.AllData(data =>
                {
                    if (sawMDAT)
                        throw new Exception("VTT cues in mp4 with multiple MDAT are not currently supported");
                    sawMDAT = true;
                    rawPayload = data;
                }))
                .Parse(dataSeg,/* partialOkay= */ false);

            if (!sawMDAT && !sawTFDT && !sawTRUN)
            {
                throw new Exception("A required box is missing");
            }

            var currentTime = baseTime;
            var reader = new BinaryReader2(new MemoryStream(rawPayload!));

View on GitHub (pinned to e113dee70c)