Recover failing MPEG-DASH sessions
without guessing.
A working guide to the failure patterns Streamwake catches on real MPEG-DASH sessions — from manifest parse and profiles/type coherence through SegmentTemplate $Number$ numbering, SegmentTimeline t/d access-unit consistency, sidx parse failures, and the availabilityTimeOffset misuse that breaks low-latency DASH — written so an on-call engineer can read it next to a recent MPD capture and act on it.
What Streamwake checks
Five families of probes, each with a small, deterministic pass / fail / warn verdict that feeds the timeline. Every check has a name — that's the name you'll see on the agents feed.
- Root MPD returns HTTP 200 within the probe budget (default 5s); well-formed
application/dash+xml. @profilesand@typecoherent —isoff-live:2011withtype="dynamic"; no BOM.- Single-root
MPD+ xlink resolved before the agent emits a verdict — no half-parsed manifests in the timeline.
SegmentTemplate+SegmentListconsistency inside a Period — never both.$Number$resolved monotonically;startNumberdrifts flagged via segment.numbering_drift.SegmentTimelineS@t/r/daccess-unit consistency + availabilityTimeOffset respected across Representations.
@bandwidthascending per AdaptationSet — descending ladders resist ABR descent when the cohort buffers.BaseURLresolve-rate across every Period — broken BaseURLs raise a global 404 probe before any SegmentTemplate URI ships.- Catches the case where a stealth republisher swaps a Representation in late with a different
@codecs/@bandwidththan the original ladder.
$Number$jumps in the trailing window — gap > 1 fails the probe even when the player keeps streaming.startNumberreset against a CDN that still hosts the prior counter — catches the startNumber drift that segment.tail_window misses.- Pairs with
window_anchor_advancedon the publishTime / timeShiftBufferDepth axis — a number reset without a window advance is a rebase the cohort cannot recover from.
- sidx box present when
SegmentBase@indexRangedeclares it — its references must resolve inside the init URL byte range. - Cross-checks the sidx
ref_sizedurations against SegmentTimelineS@durationon the parent Representation. - Most common cause of “first segment has no moof”: sidx references resolve out-of-line against a subsegment that the republisher is mid-rewriting.
Anatomy of an MPD
A live DASH MPD with three video Representations (360p / 720p / 1080p) plus an audio AdaptationSet, exact SegmentTimeline S@t/d bounds, an explicit startNumber on every Representation, an AvailabilityTimeOffset on the audio set, and a sidx box trace from the 720p indexRange. The annotations below name the probe that reads each element.
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011"
type="dynamic"
minimumUpdatePeriod="PT2S"
timeShiftBufferDepth="PT12S"
availabilityStartTime="2026-08-20T10:00:00.000Z"
publishTime="2026-08-20T11:42:13.000Z"
profiles="urn:mpeg:dash:profile:isoff-live:2011">
<BaseURL>https://cdn.example.com/live/event/</BaseURL>
<Period id="P0" start="PT0S">
<AdaptationSet contentType="video" mimeType="video/mp4" segmentAlignment="true" startWithSAP="1">
<Representation id="v1" bandwidth="628000" width="640" height="360" codecs="avc1.640028">
<SegmentTemplate timescale="1000"
initialization="video/360p/init.mp4"
media="video/360p/seg-$Number$.m4s"
startNumber="7421"
presentationTimeOffset="0">
<SegmentTimeline>
<S t="3043812000" d="6000" r="2"/>
<S t="3043824000" d="6000"/>
<S t="3043830000" d="6000"/>
<S t="3043836000" d="6000"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="v2" bandwidth="2428000" width="1280" height="720" codecs="avc1.640028">
<!-- sidx-shaped Representation: indexRange returns bytes from
inside an sidx box that lives in a subsegment the packager
is also rewriting for low-latency delivery. -->
<SegmentTemplate timescale="1000"
initialization="video/720p/init.mp4"
media="video/720p/seg-$Number$.m4s"
startNumber="7421">
<SegmentTimeline>
<S t="3043812000" d="6000"/>
<S t="3043818000" d="6000"/>
<S t="3043824000" d="6000"/>
<S t="3043830000" d="6000"/>
<S t="3043836000" d="6000"/>
</SegmentTimeline>
</SegmentTemplate>
<!-- SegmentBase indexRange is OPTIONAL in DASH; when present it
MUST reference an sidx box that lives inside the
initialization URL’s byte range, not inside a media
subsegment that can be rewritten. -->
</Representation>
<Representation id="v3" bandwidth="4828000" width="1920" height="1080" codecs="avc1.640028">
<SegmentTemplate timescale="1000"
initialization="video/1080p/init.mp4"
media="video/1080p/seg-$Number$.m4s"
startNumber="7421">
<SegmentTimeline>
<S t="3043812000" d="6000"/>
<S t="3043818000" d="6000"/>
<S t="3043824000" d="6000"/>
<S t="3043830000" d="6000"/>
<S t="3043836000" d="6000"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
<AdaptationSet contentType="audio" mimeType="audio/mp4" lang="en">
<Representation id="a1" bandwidth="128000" codecs="mp4a.40.2">
<SegmentTemplate timescale="1000"
initialization="audio/init.mp4"
media="audio/seg-$Number$.m4s"
startNumber="7421"
presentationTimeOffset="0">
<SegmentTimeline>
<S t="3043812000" d="6000" r="4"/>
</SegmentTimeline>
</SegmentTemplate>
<AvailabilityTimeOffset value="2.0"/>
</Representation>
</AdaptationSet>
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-xsdate:2014" value="https://time.example.com/utc"/>
</Period>
</MPD>GET https://cdn.example.com/live/event/video/720p/seg-7423.m4s
HTTP/1.1 206 Partial Content
content-type: video/iso.segment
content-range: bytes 0-98304/412800
range: bytes=0-98304
ftyp + moov + sidx (offset 0x10C0–0x18FF, 4 references)
ref[0] type=1 size=98304 SAP=1 starts_at=0
ref[1] type=1 size=96608 SAP=1 starts_at=98304
ref[2] type=1 size=98304 SAP=1 starts_at=194912
ref[3] type=1 size=98560 SAP=1 starts_at=293216
moof (mdat starts at offset 0x20FC) but the sidx references resolve
to byte ranges that overlap the rewritten subsegment body that the
low-latency republisher is mid-flight on. sidx.parse_ok fails on
the box-cross-check; first-segment .m4s has no moof at the
declared offset.- manifest.well_formed → root
MPD@type+MPD@profiles - segment.tail_window →
SegmentTimeline S@t/d - segment.numbering_drift →
SegmentTemplate@startNumber+$Number$sequence - sidx.parse_ok →
SegmentBase@indexRange+ sidx box bytes - availability_offset.coherent →
AvailabilityTimeOffset@valueon every AdaptationSet - ladder.bandwidth_order →
Representation@bandwidthascending inside AdaptationSet
The MPD declares a single Period with two AdaptationSets — video (three ordered Representations) and audio. Every Representation uses SegmentTemplate with $Number$ + an explicit startNumber. The probe segment.numbering_drift reads startNumber + the resolved $Number$ sequence on every reload.
The SegmentTimeline under 360p repeats one S element twice with r="2"; the other video reps do the same. The audio AdaptationSet repeats one S element four times with r="4". Cross-rep S@t + presentationTimeOffset values must align — that is what segment.tail_window asserts.
The audio AdaptationSet emits <AvailabilityTimeOffset value="2.0"/> — the video reps omit it (defaults to 0). The probe availability_offset.coherent reads this delta and warns when audio + video disagree on the offset; window_anchor_advanced reads the matching publishTime / timeShiftBufferDepth advance.
The 720p sidx trace on the right shows the failure mode the budget mostly pays for: the sidx box lives inside a subsegment byte range (0x10C0–0x18FF) that the republisher is mid-rewriting. sidx.parse_ok catches the box-cross-check failure before MSE does, and sidx.references_inline fails because the references resolve out-of-line against the init URL. The probe order in the timeline puts sidx.verify right after segment.tail_window so the cross-element drift surfaces as a single, named verdict.
The probe shape is small because the spec is mature: parse, well-formed, segment-head, segment-tail, ladder order, availability offset, sidx parse, numbering drift. Eight probes cover most of what an on-call engineer needs for an attach in 2026. Every probe feeds a verdicts stream that the agentic-ops layer classifies under one of a small handful of heuristics below.
Ten ways a MPEG-DASH session breaks
Each row: symptom the agent reports → underlying cause → a fix that holds under the next probe cycle. The probe names are what you'd grep for in the agents feed.
Running players — already on segment $Number$=7421 — see the next manifest relist with startNumber=1 and the join cohort sees a fresh timeline. Long-running sessions either drop silently to a 404 on the URI resolution the player built from the old Number, or the live-edge stalls because the news index has nothing the player trusts above $Number$=1.
The packager restarted without draining the prior Number counter. MPD@7422 ships with startNumber="1", SegmentTemplate has no $Number$ inheritance guard, and the previous tail-window probes reference $Number$ values the packager no longer publishes. segment.numbering_drift catches it; window_anchor_advanced trips as a secondary because publishTime moves even though the timeShiftBufferDepth did not advance.
On any packager restart, emit a fresh Period (`<Period id="P1">`) instead of resetting startNumber; or, if you cannot change the Period boundary, preserve Number monotonicity by keeping the prior packager warm long enough to drain the trailing window. Streamwake coerces the agent to replay the prior window the moment segment.numbering_drift fires.
A graceful primary→secondary packager hot-swap lands cleanly. The new MPD skips from $Number$=8210 to $Number$=9100 — the secondary’s startNumber was tuned to “hundreds after the primary” instead of "the exact next Number". A cohort rendering bounds checking catches the gap and stalls on the seam; ABR locks the lowest rung for the cohort that joined across the swap.
Secondary packager startNumber was set conservatively to “a few hundred above where the primary was last seen” — it adds headroom but breaks `$Number$` monotonicity from the player’s perspective. The probe segment.numbering_drift trips on any gap > 1; window_anchor_advanced trips if the gap opens a hole the manifest does not cover in the trailing 6 segments.
Read startNumber from the last successful MPD@tail on the primary side and seed it into the secondary before hot-swap; or, more robustly, use a shared counter (Redis / etcd) so the secondary computes startNumber from the same clock the primary writes. Streamwake asserts startNumber continuity across the trailing 6 segments and trips segment.numbering_drift the moment a gap > 1 appears.
MPD parses green. Players either reject the first segment with “no moof box at declared offset” or report QuotaExceededError on the first .m4s append. Logs show the byte range returned 200 but the box at the offset is not what the player expected. Republisher that emits low-latency subsegments is the suspect.
SegmentBase@indexRange on the v2 Representation points at byte 0x10C0–0x18FF — the sidx box lives inside a subsegment that the republisher is also writing for low-latency delivery. Half the time the sidx is intact, half the time the republisher has overwritten the subsegment body behind the player. sidx.parse_ok fails on the box-cross-check; sidx.references_inline fails because the references resolve out-of-line relative to the init URL.
Co-locate the sidx box inside the byte range of the init URL (SegmentBase@indexRange must NOT cross media-segment boundaries). When streaming live with low-latency rewriting, use SegmentTemplate + SegmentTimeline — sidx is unnecessary, and $Number$ gives you the deterministic address you need. Streamwake fails the attach on the next probe cycle once the sidx box-cross-check fails.
On the first request the player thinks every segment in `timeShiftBufferDepth` is "available now" — even ones the packager has not actually landed yet. The player fetches segment 7423 before the packager publishes it, gets 404, and ABR descends the ladder. Latency reads as 9–12s because the player is chasing a manifest ahead of the byte stream.
The packager declares a low-latency DASH workflow (small timeShiftBufferDepth, short minimumUpdatePeriod) but does not emit `<AvailabilityTimeOffset>` on the AdaptationSet. Default value is 0, which means "all segments referenced inside the available window are ready immediately." For a low-latency republisher that is wrong: the implementation needs a positive offset (typically equal to the manufacturing-server publish cadence) for the legal "available-after-this-much-time" semantics. window_anchor_advanced trips because the player is consuming window anchor faster than the bytes land; availability_offset.coherent flips to fail (or warn).
On any low-latency DASH AdaptationSet, emit `<AvailabilityTimeOffset value="X"/>` where X ≥ the manufacturing-server publish cadence; mirror the same value on every Representation inside the AdaptationSet so audio and video agree. Streamwake asserts availability_offset.coherent on $Number$ + SegmentTimeline t/d alignment.
The player lets a segment sit marked "available" for X seconds longer than the packager has actually published it. Fetch logs show the player doing GETs that return 404 against freshly published media; ABR keeps the lowest rung because buffer math underflows. Latency appears to spike to 15+ seconds when in fact bytes are arriving on time.
A misconfigured packager signs availabilityTimeOffset positive (the spec uses a floating-point offset where positive = "available in PAST relative to a wallclock anchor") by default on every segment, including the live-edge segment that is "available NOW." The probe availability_offset.coherent uses the MPD’s own UTC timing reference against SegmentTimeline starts to detect the sign flip. once the offset flips against a live-edge segment, the player treats it as not-yet-available when it is.
Compute `availabilityTimeOffset` from the live-edge SegmentTimeline start against publishTime, not against the wallclock; emit 0 on the live-edge segment and a strictly positive value only on segments the packager is not yet ready to commit. Verify with availability_offset.coherent on the next MPD reload — the probe should flip to pass within one cadence window of the fix.
A long-running session reaches the next Period boundary, the player tries to fetch the first `.m4s` of $Number$ from the new Period, gets HTTP 404. The previous Period’s tail had no segment with that URI on origin — the new Period has its own BaseURL and the player built the URI from MPD@old. ABR drops to the bottom rung while the seek range catches up.
On Period transition, Representations did not preserve the address shape — the new Period’s $Number$ starts at 1 but the player seeks to $Number$=7421 and asks for `video/720p/seg-7421.m4s` relative to the new BaseURL. The packager has no such URI. segment.numbering_drift fires on the period hop; segment.tail_window fires on the trailing gap.
Either restart the new Period at the prior $Number$+1, or emit an xlink:href into the new Period that resolves against the prior adress. Streamwake asserts window_anchor_advanced on every period boundary and pre-rolls the trailing window so the cohort does not pay the cold-cache tax on the first segment.
Players joining mid-stream land on a manifest where SegmentTemplate @media is `video/720p/seg-$Number$.m4s` but startNumber has just been reset to 1 — yet the partner CDN still has catches named `seg-7421.m4s` on origin (the prior packager’s run). The player fetches `seg-1.m4s`, gets 404, ABR drops the rung.
Same root as the package-restart case but a more aggressive form: the packager decides to "fresh-Number" on a reload but the CDN cache layer still hosts the prior counter. The byte stream at the new address is unrelated to what the byte stream at the old address was — yet the segment hash_drift probe has nothing to compare against because the URIs differ. segment.numbering_drift catches the discontinuity; window_anchor_advanced catches the resulting seek-range mismatch.
On any Number reset, invalidate the cache for every URI under the old counter (`PURGE` on the affected path globs) before relisting startNumber; or, never re-Number during a live event. Streamwake auto-fails the attach on the next probe cycle if the manifest declares startNumber=1 against a CDN that still hosts the previous counter. See the working postmortem at /incident-lab/origin-shield-saturation-correlated-cache-miss-storm — when segment-number-cohort cache-miss posture + origin-shield fan-in co-fire, the queue depth climbs and the cohort is the symptom, the upstream cache-tier is the cause.
Player parses the .m4s bytes; MSE throws `SourceBuffer.appendBuffer` with a track-brand error: the sidx box exists but the major brand in the ftyp box excludes the `msdh`/`msix` track identifier that MSE requires to walk the sidx references. Player falls back to no-segmentation mode and ABR oscillates between rungs.
The packager authored the sidx box but the ftyp brand set in the same media segment is `isom + dash + cmfv` — enough for plain playback but missing the Sidx-aware track identifier MSE looks for when the brand is declared. sidx.parse_ok catches the ftyp/sidx mismatch before MSE does; sidx.references_inline fails because the brand mismatch blocks the append.
Align the ftyp brand set on every media segment so it includes both `msdh` and `msix` whenever the same track publishes an sidx box; have the packager treat this as a packaging lint. Streamwake fires sidx.parse_ok on the next probe cycle the moment the brand set clears.
MPD parses green, but the player believes the segment is 6.0s while the actual bytes only carry 5.7s of media — ABR thinks the buffer underflows halfway through the segment, DESCENDS the ladder, ascends again, and reproduces that oscillation on every cadence. Tail viewers report a half-second rebuffer every 8–10 seconds.
The sidx subsegment durations add up to a $Duration$ that disagrees with the SegmentTimeline `S@t/r/d` on the parent Representation. Cross-element drift — the player schedules the next fetch by $Duration$ but the byte stream only carries what the sidx boxes advertise. segment.tail_window fires because tail-window-bytes is wrong against the declared timeline; sidx.parse_ok fires as the secondary because the box-carve does not match the timeline.
Emit sidx subsegment durations that sum to the SegmentTimeline `S@duration` on the parent Representation; refuse to publish until sum(S@d) == Sum(sidx.ref_size) / $Timescale$. Streamwake pairs sidx.parse_ok with segment.tail_window so the cross-element drift surfaces as a single verdict.
A cohort that joined the live edge starts seeing one of two regression patterns depending on player: native dash.js refetches every segment from the start because it interprets the offset as "the window moved"; shaka-player holds the tail and stops advancing, and latency creeps from 4s to 18s.
`availabilityTimeOffset` was tuned too aggressively for the manufacturing-server cadence — segments the player expected to be ready are still not ready. The player compensates by seeking forward past the offset, which pulls the seek window into the past without ripping forward. window_anchor_advanced trips on the first mismatch; availability_offset.coherent flips as the secondary probe.
Tune offset to match the manufacturing-server’s actual publish cadence (median over the last N windows, not the worst case); re-tune at the next MPD reload. Streamwake asserts availability_offset.coherent + window_anchor_advanced on the same probe cycle so the loop closes on a single cadence.
Heuristics: how the agent loop classifies the incident
The probe families above produce verdicts. Three rules in the agentic-ops layer turn a stream of verdicts into an incident classification — without a human reading the timeline.
- Rule: TTFB p95 across the last N probes vs the phased baseline for the same cohort + region-of-origin.
- What it surfaces: origin egress saturation on sidx-bearing Representations and shipping-edge cold-cache fills on SegmentTemplate URI families — long before any segment actually 4xxs.
- Agent does next:raise probe cadence for this stream, prepend to the alert feed, and open an incident tagged "egress_pressure" — actionable without waiting for a rebuffer report.
- Rule:in-flight fetches on the outbound pool vs the cohort’s expected fanout; pool-utilization > 85% over more than one cadence.
- What it surfaces: a DASH join storm where every player fetches init.mp4 + sidx + the head of SegmentTimeline in parallel — pins a CDN edge that was sized for steady-state.
- Agent does next: auto-throttle probe fan-out for this stream, meta-classify as "join_storm vs regional_skew", and emit a routing suggestion rather than a rebuffer alarm.
- Rule:gap > 1 in the resolved $Number$ sequence on any Representation across the trailing 6 segments; packager restart + startNumber reset pairs with a non-advanced publishTime.
- What it surfaces: a packager-restart Number drop, a hot-swap gap, or a Period boundary the representations did not inherit Number across — all of those break the trailing 6-segment invariant.
- Agent does next:classify "packager_number_drift", mark the affected stream as affected-attached, and force a fresh MPD reload + replay of the trailing window on the next cadence — closes the loop that a passive monitor cannot.
The failure-mode rows above trace back to the three heuristics: sidx parse failures on a republishing stack surface as TTFB drift on the sidx-bearing representations before they surface as MSE append errors; availabilityTimeOffset misuse on low-latency DASH looks like fetch-concurrency exhaustion the moment the cohort joins at peak; packager restarts and Period boundaries surface as segment-numbering drift before any header-level probe notices. A passive-monitor logs the same verdicts and waits for a rebuffer report to fire — by which point you're already paying the cohort-trust cost and writing the postmortem. The agent loop flips the polarity: classify the verdict under one of the three heuristics, remediate by replaying the trailing window or rebalancing the sidx republisher, then verify that the next probe cycle clears with availability_offset.coherent in green. Each heuristic closes a loop that a passive monitoring pipeline cannot.
Diagnose with Streamwake
Register the MPEG-DASH source against POST /api/v1/streams with protocol: DASH, then read the agent timeline back through GET /api/v1/agents. The probe verdicts in the timeline are exactly the rows above.
The curl below registers a live DASH source URL and asks for a 30-second probe cadence. The "agents" list is what makes the agent run the manifest-reachable, numbering-drift, sidx-parse, and availability-offset probes on every rebuild — those four are the brief-driven probes the timeline above calls out by name.
The cookie is the same better-auth.session_token that gates every /api/v1/* call — see the auth guide for how to mint one.
Once the stream is registered, the agents endpoint returns the per-probe verdicts below. The order of checks mirrors the probe families in the section above — manifest.reachable first, then well-formed + SegmentTemplate + SegmentTimeline, then the new brief-driven probes (segment.numbering_drift, sidx.parse_ok, availability_offset.coherent), with the heuristic layer fetch.ttfb_drift + fetch.concurrency last because those are the ones that close the loop with the cohort.
curl -X POST https://streamwake.polsia.io/api/v1/streams \
-H "content-type: application/json" \
-b "better-auth.session_token=<your-session-cookie>" \
-d '{
"sourceUrl": "https://cdn.example.com/live/event/manifest.mpd",
"protocol": "DASH",
"probeIntervalSeconds": 30,
"agents": [
"manifest.reachable",
"manifest.well_formed",
"segment.head_window",
"segment.tail_window",
"segment.numbering_drift",
"window_anchor_advanced",
"sidx.parse_ok",
"sidx.references_inline",
"availability_offset.coherent",
"ladder.bandwidth_order",
"fetch.ttfb_drift",
"fetch.concurrency"
]
}'curl https://streamwake.polsia.io/api/v1/agents?stream_id=<id> \
-b "better-auth.session_token=<your-session-cookie>"{
"stream_id": "cklivedashevent205",
"source": "https://cdn.example.com/live/event/manifest.mpd",
"protocol": "DASH",
"profiles": "urn:mpeg:dash:profile:isoff-live:2011",
"checks": [
{
"probe": "manifest.reachable",
"result": "pass",
"latency_ms": 138,
"detail": "content-type application/dash+xml"
},
{
"probe": "manifest.well_formed",
"result": "pass",
"detail": "MPD type=dynamic; profiles match isoff-live:2011; no BOM; SegmentTemplate + SegmentTimeline present"
},
{
"probe": "segment.head_window",
"result": "pass",
"latency_ms": 92,
"segments_checked": 6
},
{
"probe": "segment.tail_window",
"result": "pass",
"detail": "SegmentTimeline S@t/d monotonic; presentationTimeOffset=0 on every Representation; PDT projection contiguous"
},
{
"probe": "ladder.bandwidth_order",
"result": "pass",
"representations_checked": 3,
"detail": "AdaptationSet v1/v2/v3 sorted ascending (628K / 2428K / 4828K); BaseURL resolve-rate 100% across 1 Period"
},
{
"probe": "segment.numbering_drift",
"result": "fail",
"detail": "Numbering jumped 7421 → 1 across a packager restart; startNumber reset to 1 in MPD@7422; tail-window probe integrity broken until next reload + replay"
},
{
"probe": "window_anchor_advanced",
"result": "warn",
"detail": "MPD publishTime advanced, but availabilityStartTime + timeShiftBufferDepth did not move forward; live edge stalled at 9.8s even though segments kept landing"
},
{
"probe": "sidx.parse_ok",
"result": "fail",
"detail": "SegmentBase indexRange on v2 references 0x10C0–0x18FF; sidx box exists but 2/4 SAP=1 references overlap a subsegment that the packager is mid-rewriting for low-latency"
},
{
"probe": "sidx.references_inline",
"result": "fail",
"detail": "4/4 sidx reference byte ranges resolved out-of-line (cross media segment, not inside init URL); MSE rejects first append — sidx inline-vs-out-of-line contract violated"
},
{
"probe": "availability_offset.coherent",
"result": "warn",
"detail": "audio AdaptationSet declares AvailabilityTimeOffset value=2.0 (positive = 'available past now'); video AdaptationSet omits it (defaults to 0 = 'available now'); A/V drift on join"
},
{
"probe": "fetch.ttfb_drift",
"result": "warn",
"detail": "TTFB p95 1180ms vs phased baseline 360ms — egress saturation suspected on sidx-bearing Representations"
},
{
"probe": "fetch.concurrency",
"result": "pass",
"detail": "outbound fan-out 64% of pool ceiling — within tolerance"
}
]
}Read the protocol-by-protocol pair
The MPEG-DASH guide above covers the manifest-parse + SegmentTemplate + SegmentTimeline + sidx + availabilityTimeOffset probes that apply to every DASH workflow. The peer troubleshooting surface at /troubleshooting/mpeg-dash covers the broader catalog — late-join stalls, 404 storms, ABR oscillation, Period / MSE / DRM edges, duration drift, and multi-CDN signing-region issues. The HLS guide is the protocol-by-protocol sibling, and the LL-HLS guide is the low-latency HLS arm of the pair.
Want Streamwake to catch this on its own?
Sign up, register a DASH source with protocol: DASH, and the same manifest-parse + SegmentTimeline + numbering-drift + sidx-parse + availability-offset + ladder-order probes that produced the timeline above run on every cadence — and surface in a Slack channel, a webhook, or the streams dashboard.
- Stream list reads from
GET /api/v1/streams; per-stream timeline fromGET /api/v1/agents. - Probe verdicts stream into the dashboard within one cadence interval — manual curl not required.
- Self-serve signup at /sign-up — no sales call required for the first stream.
- Pick a recent on-call incident — manifest stall, edge miss, player-side stall, or peer congestion.
- We replay it through the same reliability-agent probe cascade used on the postmortem above.
- You walk away with a written what-could-have-been-Automated readout, not a sales deck.
Related writeups
The closest siblings cover the cache-tier / origin-shield fan-in failure-mode shapes — particularly the correlated upstream cache-miss storm where the cohort stays CALM while the upstream origin-shield fans into the tier past its pre-provisioned ceiling, classified at 84% with cdn_edge_pop_warmup_under_eviction ruled out by name on warm edge POP segment-leg cache-hit. Recovery verified cohort-side + shield-side across four staged gates T+30 s → T+15 m, NOT infrastructure-green.