Current Behavior
Tracks intermittently fail to play (Windows debug and Android APK), with
media_kit/MPV logging:
[MediaKitError]
Failed to open http://localhost:PORT/stream/<trackId>.
and the local proxy server logging:
Bad state: No element
#0 ListBase.firstWhere
#1 ServerPlaybackRoutes._getSourcedTrack (playback.dart)
#2 ServerPlaybackRoutes.getStreamTrackId (playback.dart)
Root cause 1 — race condition in _getSourcedTrack
_getSourcedTrack looked up the requested media in
audioPlayer.playlist.medias by matching request.requestedUri:
final media = audioPlayer.playlist.medias
.firstWhere((e) => e.uri == request.requestedUri.toString());
MPV issues the HTTP request to the local proxy before the Dart-side
player state (audioPlayer.playlist.medias, from media_kit) has
synchronized with the native player state. In that window medias does
not yet contain the requested URI, so firstWhere throws
Bad state: No element → the server returns 500 → MPV reports
"Failed to open". This is why the issue sometimes "resolved itself" after
a moment (once Dart-side state caught up) without an app restart.
Not platform-specific — server.dart, audio_player.dart, and
playback.dart's _getSourcedTrack have no per-platform branching on
this path; Android hits the race more often due to slower startup
(wider timing window).
Root cause 2 — cache file corruption under concurrent range requests
Separately, after fixing root cause 1, caching threw:
PathAccessException: Cannot rename file to '...track....m4a',
path = '...m4a.part'
(OS Error: The process cannot access the file because it is being used
by another process, errno = 32)
MPV opens several concurrent range requests for the same track
(buffering, seeking, reconnects can produce 2-3 simultaneous requests for
one track). Each request opened its own openWrite sink to the same
.part file in writeOnlyAppend mode. When one request finished
(onDone) and called rename, another still held the file open, so
Windows blocks the rename (errno 32). Concurrent sinks also appended to
each other, corrupting the cache.
Steps to reproduce
- Launch Spotube (Windows debug build or Android APK).
- Play a track from a playlist/queue.
- Observe intermittent
Bad state: No element in server logs / "Failed
to open" in MPV logs, and (with music caching enabled) intermittent
PathAccessException on cache rename.
Suggested fix
_getSourcedTrack: stop depending on audioPlayer.playlist.medias
entirely — resolve the track directly from playlist.tracks (Riverpod
state) by trackId, using firstWhereOrNull + null check instead of
a throwing firstWhere.
- Cache writer: guard with a
Set<String> _cachingInProgress so only one
request caches a given track at a time (other concurrent range requests
stream through without writing to cache); only cache full requests
(contentRange.start == 0); use FileMode.writeOnly (truncate) instead
of writeOnlyAppend; wrap onDone in try/catch/finally so the
in-progress marker and incomplete .part file are always cleaned up.
Operating System
Windows, Android
Spotube version
v5.1.2+45 (current master)
Self grab
Current Behavior
Tracks intermittently fail to play (Windows debug and Android APK), with
media_kit/MPV logging:
and the local proxy server logging:
Root cause 1 — race condition in
_getSourcedTrack_getSourcedTracklooked up the requested media inaudioPlayer.playlist.mediasby matchingrequest.requestedUri:MPV issues the HTTP request to the local proxy before the Dart-side
player state (
audioPlayer.playlist.medias, from media_kit) hassynchronized with the native player state. In that window
mediasdoesnot yet contain the requested URI, so
firstWherethrowsBad state: No element→ the server returns 500 → MPV reports"Failed to open". This is why the issue sometimes "resolved itself" after
a moment (once Dart-side state caught up) without an app restart.
Not platform-specific —
server.dart,audio_player.dart, andplayback.dart's_getSourcedTrackhave no per-platform branching onthis path; Android hits the race more often due to slower startup
(wider timing window).
Root cause 2 — cache file corruption under concurrent range requests
Separately, after fixing root cause 1, caching threw:
MPV opens several concurrent range requests for the same track
(buffering, seeking, reconnects can produce 2-3 simultaneous requests for
one track). Each request opened its own
openWritesink to the same.partfile inwriteOnlyAppendmode. When one request finished(
onDone) and calledrename, another still held the file open, soWindows blocks the rename (errno 32). Concurrent sinks also appended to
each other, corrupting the cache.
Steps to reproduce
Bad state: No elementin server logs / "Failedto open" in MPV logs, and (with music caching enabled) intermittent
PathAccessExceptionon cache rename.Suggested fix
_getSourcedTrack: stop depending onaudioPlayer.playlist.mediasentirely — resolve the track directly from
playlist.tracks(Riverpodstate) by
trackId, usingfirstWhereOrNull+ null check instead ofa throwing
firstWhere.Set<String> _cachingInProgressso only onerequest caches a given track at a time (other concurrent range requests
stream through without writing to cache); only cache full requests
(
contentRange.start == 0); useFileMode.writeOnly(truncate) insteadof
writeOnlyAppend; wraponDoneintry/catch/finallyso thein-progress marker and incomplete
.partfile are always cleaned up.Operating System
Windows, Android
Spotube version
v5.1.2+45 (current master)
Self grab