Skip to content

export ts --stitch --linger exits on internal error when its catalog request lands on a publisher that just died #5347

Closed

Description

@t0ms

A concrete case for quest/m1/dropped-sources.md,
with one consequence the quest doesn't list: ts::Follower exempts Dropped from failing loud, but a
drop forwarded by a relay arrives as internal error, so the exemption can only match locally.

On main at 90500867d, moq export ts --delay 1s --linger 60s --stitch on the default
moq-lite-06 exited 1 with moq: internal error in 1 of 4 crash-replace runs; the other three
resumed at every replacement. Rig: one relay on loopback, the export started first, then three
moq import ts sessions of one broadcast, 15 s each, the first two SIGKILLed 5 s before their end and
each replaced 3 s later. Default idle timeout.

  • Session 0 is killed at 10:22:34.0; session 1 connects at 10:22:37.3 and is killed at 10:22:47.20.
  • At 10:22:46.95 the relay times session 0 out and ends the export's subscriptions; the export logs
    broadcast ended, waiting for it to return at 10:22:47.26 and sends a catalog track info request.
  • The relay routes it to session 1, dead for 60 ms but not yet timed out. At 10:22:57.26 it times
    session 1 out and logs track info error … track=catalog.json err=dropped; the export logs
    track info failed … err=internal error and exits. Session 2, up since 10:22:50.5, would have
    served the return.

Error::Dropped has no arm in From<&Error> for StreamError, so it goes out as Internal, and
State::Following in rs/moq-mux/src/container/ts/follower.rs treats any resolve error but
Unroutable | Dropped as a refusal from a live instance. If the quest ends this path with
Unroutable, as it plans for requests a front aborts, the follower resumes here unchanged; the
quest's regression could cover a request in flight when the upstream session times out. On
moq-lite-07 the replacement's epoch ends the old broadcast first, so we did not see it there.

(Written by Claude Opus 5.5)

Activity

  1. added
    questBeing tracked/planned in a quest. See `quest/`
    on Oct 12, 2026
  2. kixelated commented on Oct 12, 2026

    @kixelated
    Collaborator

    Confirmed, and we've folded this into quest/m1/dropped-sources.md (planned in #5358) with two changes from what the issue anticipates.

    • Root cause: a Dropped that reaches the wire is a bug, since some path never ended with a proper error. On epochless lite-06, the front ended with Dropped when its only route timed out. A route that fails under a request or track (the upstream session dies, and nothing replaces it) now ends downstream with Unroutable. Every error variant gets a real wire mapping instead of the Internal catch-all.
    • Follower: matching on Unroutable alone would not have fixed this, because a remote Unroutable arrives as Error::Stream(StreamError::Unroutable). Rather than match errors at all, ts::Follower becomes announcement-driven while it lingers. A resolve error sends it back to waiting for the next announcement. A new announcement cancels an in-flight resolve, so it no longer sits out a dead session's idle timeout. --linger expiry is the only exit, and it fails with the last resolve error if there was one.

    Anyone who wants to donate tokens can run /quest-start quest/m1/dropped-sources.

    (Written by Claude Opus 5.5)

  3. added 3 commits that reference this issue on Oct 16, 2026
    700f1a9
    b2ef67f
    c5fef46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questBeing tracked/planned in a quest. See `quest/`

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions