Skip to content

fix: retry an audio chunk whose body breaks off or stalls - #426

Open
vLX42 wants to merge 1 commit into
devgianlu:masterfrom
vLX42:fix/chunk-read-retry
Open

vLX42 wants to merge 1 commit into
devgianlu:masterfrom
vLX42:fix/chunk-read-retry

Conversation

@vLX42

@vLX42 vLX42 commented Oct 21, 2026 •

Copy link
Copy Markdown

Resuming the WAN Show episode "I Need To Start Ad Blocking - WAN Show October 16, 2026" (spotify:episode:53W7y9ml2LKRQ6rAoQb6nf, ~3.5h, 230 chunks) from my phone at 1h51m failed to load. The seek lands on chunk 120, the CDN starts the 206 and then stalls, and the read runs into the 30s client timeout:

level=warning msg="failed load spotify:episode:53W7y9ml2LKRQ6rAoQb6nf" error="failed creating stream for spotify:episode:53W7y9ml2LKRQ6rAoQb6nf: failed seeking stream: failed reading chunk: failed reading chunk 120: failed reading chunk 120: context deadline exceeded (Client.Timeout or context cancellation while reading body)"

Nothing is loaded after that, so the client shows no position on the timeline and a seek from the phone fails with no primary stream.

downloadChunk retried a failed request or a wrong status, but a body that broke off after the 206 was not retried, so one stalled chunk killed the whole load.

Changes in audio/chunked-reader.go:

  • downloadChunk now does the request, the status check and the body read in the existing backoff op and returns the data. A body that breaks off is retried like any other failure, same policy as before: 4 attempts, 1s apart, stops when the reader is closed.
  • Each attempt has a stall timer: no bytes for 10s (chunkStallTimeout) cancels the attempt and it is retried, instead of waiting out the 30s client timeout. A slow body that keeps sending is not affected, the timer resets on every read with data.
  • The first chunk in NewHttpChunkedReader goes through the same path, so a stall on chunk 0 of a fresh load is retried too.

Tests:

  • TestFetchChunkRetriesABrokenOffBody: first body fails mid-read, second succeeds.
  • TestFetchChunkRetriesAStalledBody: first body never sends a byte, the stall timer kicks in and the retry succeeds.
  • TestNewReaderRetriesABrokenOffFirstChunk: same for chunk 0.
  • TestFetchChunkDoesNotMultiplyStatusRetries: a 403 still takes exactly 4 requests.

The first three fail on master (the stall one hangs). Existing close/backoff tests pass unchanged. go test -count=1 -tags "test_unit test_integration" ./... passes on macOS.

Also ran the daemon with this on macOS against the same episode: resume at 1h51m, then seeks to 2h22m, 33m and 1h31m from the phone all loaded within a second and played. The CDN didn't stall in that run, so the retry path itself is only covered by the tests above.

downloadChunk retried a failed request or an unexpected status, but
once the 206 came back a body that broke off or stalled until the
client timeout failed the chunk, and with it the whole load.

Read the body inside the same backoff op, so a broken off body is
retried like any other failure, with the same 4 attempts, 1s apart.
A chunk that receives no bytes for 10s is abandoned and retried
instead of waiting out the 30s client timeout. The first chunk now
goes through the same path.
@vLX42
vLX42 force-pushed the fix/chunk-read-retry branch from 89d334c to 9adc687 Compare October 21, 2026 19:12
@vLX42 vLX42 changed the title fix: retry an audio chunk whose body breaks off mid-read fix: retry an audio chunk whose body breaks off or stalls Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant