Check your m3u8 against the spec — and against its own bytes.
Paste an HLS or CMAF playlist URL. This reads your master, follows the ladder, and opens
the first few kilobytes of each initialization segment to check that the CODECS you declare is the codec the file
actually contains. No key, no signup, no account.
Public http(s) playlists only. Nothing is stored except the playlist path and what the check concluded, for seven days — the query string is removed first, so a signed URL's token is never written down.
How the check runs
Four steps, in order, inside a fifteen-second budget. The fourth is the one most free playlist linters stop short of.
Step 1
It reads the playlist you gave it
One GET, from our servers, identified in your access log as TranscodelyHLSCheck. Master playlists and media playlists are both accepted, and a file that is neither is told so.
Step 2
It judges the text against RFC 8216
Required tags, attribute grammar, EXT-X-VERSION against the features actually in use, target duration against the segment durations, byte ranges, discontinuity sequencing, key signalling, and the low-latency tags from the 8216bis draft.
Step 3
It follows the ladder
Up to twelve variant and rendition playlists, so audio and subtitle groups are resolved against the EXT-X-MEDIA tags that are meant to define them, and each variant is read rather than assumed.
Step 4
It opens your initialization segments
A ranged GET for the first 4 KiB of each EXT-X-MAP target, then a walk of the ISO base media boxes down to the sample entry — avc1, hvc1, av01, mp4a. The codec that walk finds is compared with the CODECS string the variant declares.
What this checker does not do
Read this before the rule catalog. A checker that overstates its reach is worse than no checker, because the next person stops believing the findings that are real.
It never plays your stream
Nothing here opens a media pipeline, requests a DRM licence or renders a frame. A clean report means the playlists are well-formed and the initialization segments say what the playlists claim. It does not mean every player will play it.
It does not decode
There is no decoder in it. So it can tell you the sample entry is hvc1 where you declared avc1, and it can tell you nothing about pixel format, colour volume, HDR metadata inside the bitstream, frame-level conformance, or whether the real bitrate matches the declared BANDWIDTH.
MPEG-TS variants are not codec-probed
The CODECS comparison needs fMP4. A transport-stream codec lives in the PMT of every segment, which is a different parser over a much larger fetch, so a TS ladder gets every other check and not that one.
DASH is not checked yet
An .mpd is not parsed as DASH at all — it is read as a playlist, found not to start with #EXTM3U, and reported as not being one. The MPD rules are not written yet, and nothing here pretends they are.
It checks encryption signalling, not encryption
It reads EXT-X-KEY and tells you the method is undefined, the URI is missing, or the KEYFORMAT names no registered system. It never fetches a key and never attempts a decrypt, so "signalled correctly" is not "the keys work".
It reads part of your stream, and says when it stopped
One check reads the submitted playlist, up to twelve further playlists and up to six initialization segments, inside fifteen seconds. When a budget runs out the check stops, reports budget-exhausted, and grades the result "Partly checked" rather than passing off a partial walk as a clean stream. A ladder with many audio and subtitle renditions reaches that cap easily — the second example above is one, deliberately.
It is not Apple’s Media Stream Validator
mediastreamvalidator runs on a Mac, downloads segments and decodes them. That is a larger and different tool. If you are submitting to the App Store, run it as well as this.
Rule catalog
Every rule the checker can report, with the severity it always carries. Error means a specification MUST is violated, or a conforming player will break on it. Warning means a SHOULD is violated, or the stream works but degrades. Note is an observation. Severity belongs to the rule rather than to where it fired, so the same finding is graded the same way every time.
54 rules: 32 errors, 15 warnings, 7 notes.
| Rule | Severity | What it means |
|---|---|---|
| bandwidth-missing | Error | Variant has no BANDWIDTH BANDWIDTH is required on every EXT-X-STREAM-INF; without it a player cannot choose a variant. RFC 8216 §4.3.4.2 |
| budget-exhausted | Note | Check stopped at its fetch budget Only part of the stream was examined. Re-run against a smaller ladder for full coverage. |
| byterange-invalid | Error | EXT-X-BYTERANGE is malformed or has no offset Write n[@o]; the offset may be omitted only when the previous segment was a sub-range of the same resource. RFC 8216 §4.3.2.2 |
| codecs-malformed | Error | CODECS value is not a valid RFC 6381 string Use the registered form, e.g. avc1.640028, hvc1.2.4.L120.B0 or hvc1.1.6.H150.90, mp4a.40.2, av01.0.05M.08. RFC 6381 §3.3 |
| codecs-mismatch | Error | Declared CODECS disagrees with the initialization segment Emit the CODECS string derived from the actual sample entry; players reject or mis-select on a wrong one. |
| codecs-missing | Warning | Variant has no CODECS Declare CODECS so players can rule a variant out before fetching it. Apple requires it for App Store playback. RFC 8216 §4.3.4.2 |
| discontinuity-sequence-invalid | Error | EXT-X-DISCONTINUITY-SEQUENCE is not a non-negative integer The value must be a decimal integer of zero or more. RFC 8216 §4.3.3.3 |
| discontinuity-sequence-missing | Warning | Discontinuities without EXT-X-DISCONTINUITY-SEQUENCE A playlist that has already rolled past a discontinuity must declare the sequence number so players stay aligned. RFC 8216 §4.3.3.3 |
| empty-playlist | Error | Playlist is empty The URL returned no playlist tags. Check that it serves the .m3u8 and not an error page. |
| encryption-inconsistent | Warning | Some variants are encrypted and others are not Encrypt the whole ladder. A player that switches into a clear variant leaks the content. |
| endlist-missing | Warning | VOD playlist has no EXT-X-ENDLIST A finished playlist must end with EXT-X-ENDLIST, or players keep reloading it forever. RFC 8216 §4.3.3.5 |
| extinf-missing | Error | Segment URI has no EXTINF Every media segment URI must be preceded by its own EXTINF tag. RFC 8216 §4.3.2.1 |
| findings-truncated | Note | Not every finding is listed This playlist produced more findings than one result carries. Fix the listed ones and run the check again. |
| group-id-unresolved | Error | Variant references a group with no renditions Every AUDIO / SUBTITLES / CLOSED-CAPTIONS value must match the GROUP-ID of an EXT-X-MEDIA tag in the same playlist. RFC 8216 §4.3.4.2.1 |
| hold-back-too-small | Error | HOLD-BACK is below three target durations HOLD-BACK must be at least three times EXT-X-TARGETDURATION. Unlike PART-HOLD-BACK's three-times rule, this one is a MUST. RFC 8216bis §4.4.3.8 |
| iframe-uri-missing | Error | I-frame stream has no URI attribute EXT-X-I-FRAME-STREAM-INF carries its playlist as a URI attribute, not on the following line. RFC 8216 §4.3.4.3 |
| independent-segments-missing | Note | EXT-X-INDEPENDENT-SEGMENTS is not declared Add it when every segment starts with a keyframe; players can then switch variants without buffering a whole segment. |
| init-not-fmp4 | Warning | Initialization segment is not an ISO base media file An EXT-X-MAP target must be an fMP4 init segment starting with an ftyp box. |
| init-unparseable | Warning | Initialization segment could not be parsed The first bytes are not a readable ISO base media file. This check needs the moov box inside the first few kilobytes. |
| init-unreachable | Warning | Initialization segment could not be read The EXT-X-MAP URI did not return bytes. Check it resolves and that the origin allows range requests. |
| key-method-invalid | Error | EXT-X-KEY METHOD is not a defined value METHOD must be NONE, AES-128, AES-256, AES-256-CTR, AES-256-GCM, SAMPLE-AES or SAMPLE-AES-CTR. RFC 8216 §4.3.2.4 / RFC 8216bis §4.4.4.4 |
| key-uri-missing | Error | Encryption key has no URI URI is required on EXT-X-KEY unless METHOD is NONE. RFC 8216 §4.3.2.4 |
| keyformat-unknown | Note | KEYFORMAT names no recognized DRM system Widevine, PlayReady and FairPlay have registered identifiers. A custom KEYFORMAT only plays where it is understood. |
| ladder-bandwidth-duplicate | Warning | Two variants declare the same BANDWIDTH Give each rung a distinct BANDWIDTH; identical values make variant selection arbitrary. |
| ladder-unsorted | Note | Variants are not ordered by BANDWIDTH No specification requires an order. But a player that has not measured bandwidth yet may start on the first entry, so whichever variant you list first is your startup default — make sure it is the one you want. |
| map-missing | Error | fMP4 segments with no EXT-X-MAP CMAF/fMP4 segments need an EXT-X-MAP naming the initialization segment. RFC 8216 §4.3.2.5 |
| map-unexpected | Warning | EXT-X-MAP declared for MPEG-TS segments Transport-stream segments are self-initializing. Remove the EXT-X-MAP or switch the segments to fMP4. |
| media-playlist-unreachable | Warning | Variant playlist could not be read The variant playlist did not return readable text. Check the URI resolves and is publicly served. |
| media-sequence-invalid | Error | EXT-X-MEDIA-SEQUENCE is not a non-negative integer The value must be a decimal integer of zero or more. RFC 8216 §4.3.3.2 |
| missing-extm3u | Error | Not an HLS playlist The first line of every playlist must be exactly #EXTM3U. RFC 8216 §4.3.1.1 |
| mixed-segment-formats | Error | One playlist mixes MPEG-TS and fMP4 segments Keep one container per variant. A player that initialized for one cannot decode the other. |
| no-segments | Error | Media playlist has no segments A media playlist with no EXTINF segment is unplayable. |
| no-variants | Error | Master playlist declares no variants Add at least one #EXT-X-STREAM-INF followed by its playlist URI. |
| part-hold-back-below-minimum | Error | PART-HOLD-BACK is below twice PART-TARGET PART-HOLD-BACK must be at least twice PART-TARGET. This is the hard floor; three times is the recommendation. RFC 8216bis §4.4.3.8 |
| part-hold-back-too-small | Warning | PART-HOLD-BACK is below three part targets PART-HOLD-BACK should be at least three times PART-TARGET; below that, players stall at the live edge on any jitter. RFC 8216bis §4.4.3.8 |
| part-inf-missing | Error | EXT-X-PART without EXT-X-PART-INF A playlist containing partial segments must declare EXT-X-PART-INF:PART-TARGET. RFC 8216bis §4.4.3.7 |
| part-no-independent | Warning | No partial segment is marked INDEPENDENT=YES Mark the parts that start with a keyframe INDEPENDENT=YES; a player joining at the live edge needs one. RFC 8216bis §4.4.4.9 |
| part-target-exceeded | Error | A partial segment is longer than PART-TARGET No EXT-X-PART DURATION may exceed PART-TARGET. Raise PART-TARGET or shorten the part. RFC 8216bis §4.4.3.7 |
| playlist-kind-ambiguous | Error | Master and media tags in one playlist A playlist is either a master (EXT-X-STREAM-INF) or a media playlist (EXTINF), never both. Split them. RFC 8216 §4 |
| playlist-type-invalid | Error | EXT-X-PLAYLIST-TYPE is not VOD or EVENT The only valid values are VOD and EVENT. RFC 8216 §4.3.3.5 |
| playlist-unparsable | Error | Playlist could not be read to the end A line exceeded the length this checker reads, so everything after it was skipped. Check the playlist is line-delimited text. |
| rendition-default-conflict | Error | Group has more than one DEFAULT=YES rendition At most one rendition per group may be DEFAULT=YES. RFC 8216 §4.3.4.1 |
| rendition-default-not-autoselect | Warning | DEFAULT=YES rendition sets AUTOSELECT=NO A DEFAULT=YES rendition must also be AUTOSELECT=YES. RFC 8216 §4.3.4.1 |
| rendition-uri-missing | Error | Subtitle rendition has no URI URI is required on EXT-X-MEDIA when TYPE is SUBTITLES. RFC 8216 §4.3.4.1 |
| resolution-missing | Note | Variant has no RESOLUTION Add RESOLUTION so a player can avoid fetching a variant larger than the viewport. |
| server-control-missing | Warning | Partial segments without EXT-X-SERVER-CONTROL Low-latency playback needs CAN-BLOCK-RELOAD=YES and PART-HOLD-BACK; without them players fall back to normal latency. RFC 8216bis §4.4.3.8 |
| target-duration-exceeded | Error | A segment is longer than EXT-X-TARGETDURATION From EXT-X-VERSION 6 the comparison is EXTINF rounded to the nearest integer; below it, TARGETDURATION is a hard maximum. Raise TARGETDURATION or shorten the segment. RFC 8216 §4.3.3.1 |
| target-duration-missing | Error | Media playlist has no EXT-X-TARGETDURATION EXT-X-TARGETDURATION is required in every media playlist. RFC 8216 §4.3.3.1 |
| target-duration-oversized | Note | EXT-X-TARGETDURATION is far above the longest segment Players derive their live edge and reload cadence from TARGETDURATION; an inflated value slows both. |
| variant-uri-missing | Error | Variant has no playlist URI The line after an EXT-X-STREAM-INF must be the variant playlist's URI. RFC 8216 §4.3.4.2 |
| version-duplicate | Error | More than one EXT-X-VERSION tag A playlist must contain at most one EXT-X-VERSION tag. RFC 8216 §4.3.1.2 |
| version-missing | Warning | No EXT-X-VERSION, but version-2+ features are used A playlist with no EXT-X-VERSION is version 1. Declare the version the features need. RFC 8216 §7 / RFC 8216bis §8 |
| version-too-low | Error | EXT-X-VERSION is lower than the tags used require Raise EXT-X-VERSION to the compatibility version of the newest feature in the playlist. RFC 8216 §7 / RFC 8216bis §8 |
| vod-mutable | Error | VOD playlist carries low-latency tags EXT-X-PLAYLIST-TYPE:VOD promises the playlist never changes; partial segments and preload hints contradict it. RFC 8216 §4.3.3.5 |
Why the CODECS check is the one that matters
A playlist can be perfectly well-formed and still fail to play. The usual cause
is a CODECS attribute that
disagrees with the file behind it: a packager set to H.264 in front of an HEVC
encode, a hand-edited ladder, a profile string copied from another stream.
Safari rules the variant out before it fetches a byte, and the player reports
nothing more useful than a blank frame.
Reading the playlist text cannot find that. Reading the initialization segment can, and it costs one ranged request per variant. That is the check this page was built around. The rest of the rule catalog comes along with it.
Or have the ladder built correctly in the first place
Transcodely encodes into your own S3, GCS or R2 bucket and writes the HLS and DASH manifests with it. One API call in, a playable manifest out, at output paths you choose.
Open the quickstart