maxrave-dev / maxrave-dev/SimpMusic

Playback stops after a brief network hiccup (error 2000 is not retryable)

Open Beginner friendly
#2,429 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
11.4k
Forks
599
Avg merge
3d 18h
Merged PRs (30d)
7

Description

On 2.0.0 (versionCode 57), Galaxy S23 (SM-S911B), Android 16, stock release build.

A short network interruption during a song stops playback permanently. It does not resume when the connection comes back. Pressing play recovers it instantly, so nothing is actually broken except that the retry is never attempted.

Repro

  1. Start a playlist.
  2. While a song is still buffering (early in the track works best), turn on airplane mode for about 10 seconds.
  3. Turn it off, the connection returns within seconds.
  4. Playback stops as soon as the already-buffered audio runs out, and stays stopped.
  5. Press play: it resumes from exactly where it stopped.

I did not even do this deliberately. My phone roamed between two Wi-Fi access points on the same SSID at home:

08:38:59  ConnectivityService: registerNetworkAgent NetworkAgentInfo{network{398}   (WIFI CONNECTING)
08:39:04  ConnectivityService: [397 WIFI] disconnected
08:39:04  ConnectivityService: registerNetworkAgent NetworkAgentInfo{network{399}

DNS was unavailable for a few seconds, and the player asked for more stream data inside that window:

08:39:02  Stream: Error: Unable to resolve host "music.youtube.com": No address associated with hostname
08:39:02  Stream: Failed to resolve stream URL for TRACK_A
08:39:02  ExoPlayerImplInternal: Playback error / qt3: Source error
08:39:02      Caused by: java.io.IOException: Failed to resolve stream URL for TRACK_A
08:39:02  CrossfadeExoPlayerAdapter: Playback error: Source error
08:39:02  Player Error: Source error, code: 2000, code name: ERROR_CODE_IO_UNSPECIFIED
08:39:02  CrossfadeExoPlayerAdapter: pause() called (state: PLAYING, playWhenReady: true)
08:39:02  CrossfadeExoPlayerAdapter: State: PLAYING -> ERROR (playWhenReady=true)
08:39:02  CrossfadeExoPlayerAdapter: Pause: Called in invalid state: ERROR

It then sat at ERROR(7), position=48753, buffered position=48753 for 35 minutes with zero retry attempts, while the network was fine the whole time (music.youtube.com resolving in 21 ms).

Cause

In CrossfadeExoPlayerAdapter.onPlayerError:

val isRetryableSourceError =
    error.errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED ||  // 3001
        error.errorCode == PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS ||       // 2004
        error.errorCode == PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND           // 2005

The observed code is 2000, which is not in the set, so the retry branch is never entered and the adapter parks in ERROR. ERROR_CODE_IO_NETWORK_CONNECTION_FAILED (2001) is not there either, even though the comment directly above this expression documents it as handled.

The retry logic already works, it is just never reached

A single play command recovered it immediately:

09:20:05  state=ERROR(7),   position=48753, buffered position=48753
09:20:06  (media play key)
09:20:07  CrossfadeExoPlayerAdapter: State: ERROR -> PLAYING (playWhenReady=true)
09:20:12  state=PLAYING(3), position=51325, buffered position=155021

A fresh URL was resolved and playback resumed from the failure point, which is exactly what the existing retry path does, including its position snapshot.

Suggested fix

Add 2000 and 2001 to isRetryableSourceError, or retry the whole IO class, reusing the maxRetryCount and cachedPosition resume that already sits directly below.

Why this is worse than it looks

Any loss of connectivity triggers this, and SimpMusic manufactures its own. #2233 gets the app's network revoked while it keeps playing, and this morning that revocation produced three failed precaches in six minutes (08:30, 08:32, 08:35), one per track transition. It was provably app-targeted rather than a connectivity problem: all 52 DNS failures in that window belonged to SimpMusic and none to any other process on the phone. Those three then stopped playback one after another about 50 minutes later (#2239), each needing its own manual play. The mid-song failure above was separate, caused by the genuine 5-second Wi-Fi roam. Together that is the mechanism behind reports of "it stops every song and I keep having to press play".

Related: #2233, #2239, #2428

Track ID replaced with a placeholder. Wi-Fi lines trimmed to remove SSID, BSSID, MAC and IP.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at CrossfadeExoPlayerAdapter.onPlayerError and inspect the isRetryableSourceError check against the documented network error codes. Verify the existing retry path, including maxRetryCount and cachedPosition, is reached for transient I/O failures. Done means a brief connectivity loss retries playback and resumes from the failure point instead of remaining in ERROR.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.