Repository: ryanheise/just_audio

Issue:
setAudioSources with preload=true throws errors
<!--    ALL SECTIONS BELOW MUST BE COMPLETED   -->
**Which API doesn't behave as documented, and how does it misbehave?**
setAudioSources() is throwing errors in some use cases

**Minimal reproduction project**
```
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';

Future<void> main() async {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  late AudioPlayer _player;
  final List<UriAudioSource> _playlist1 = [
    AudioSource.uri(Uri.parse("https://everyayah.com/data/Hudhaify_64kbps/001001.mp3"),),
    AudioSource.uri(Uri.parse("https://everyayah.com/data/Hudhaify_64kbps/001002.mp3"),),
    AudioSource.uri(Uri.parse("https://everyayah.com/data/Hudhaify_64kbps/001003.mp3"),),
    AudioSource.uri(Uri.parse("https://everyayah.com/data/Hudhaify_64kbps/001004.mp3"),),
  ];

  @override
  void initState() {
    super.initState();
    _player = AudioPlayer();
    _init();
  }

  Future<void> _init() async {
    await _player.setAudioSources(_playlist1, initialIndex: 0, initialPosition: Duration.zero, preload: true);
  }

  @override
  void dispose() {
    _player.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          body: Column(
            children: <Widget>[
              Center(
                child: TextButton(
                  onPressed: () {
                    _player.seek(Duration.zero, index: 0);
                    _player.play();
                  },
                  child: Text('Play'),
                ),
              ),
              TextButton(
                onPressed: () {
                  _player.stop();
                },
                child: Text('Stop'),
              ),
            ],
          ),
          floatingActionButton: FloatingActionButton(
            child: const Icon(Icons.label),
            onPressed: () async {
              _player.stop();
              await _player.setAudioSources(_playlist1, initialIndex: 0, initialPosition: Duration.zero, preload: true);
              //_player.load();
              _player.play();
            },
          ),
        )
    );
  }
}
```

**To Reproduce (i.e. user steps, not code)**
Steps to reproduce the behavior:
1. Click on Play
2. Click on the floating button
3. See error

**Error messages**

```
Performing hot restart...
Waiting for connection from debug service on Chrome...
Restarted application in 234ms.
DartError: UnimplementedError: dispose() has not been implemented.
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 274:3       throw_
package:just_audio_platform_interface/just_audio_platform_interface.dart 182:5    dispose
package:just_audio/just_audio.dart 1771:23                                        <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 623:19               <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 648:23               <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 598:5                <fn>
dart-sdk/lib/async/zone.dart 1854:54                                              runBinary
dart-sdk/lib/async/future_impl.dart 239:22                                        handleError
dart-sdk/lib/async/future_impl.dart 963:46                                        handleError
dart-sdk/lib/async/future_impl.dart 984:13                                        _propagateToListeners
dart-sdk/lib/async/future_impl.dart 741:5                                         [_completeErrorObject]
dart-sdk/lib/async/future_impl.dart 745:5                                         [_completeError]
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 511:7                completeError
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 582:12               _asyncRethrow
package:just_audio_web/just_audio_web.dart 34:33                                  <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 623:19               <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 648:23               <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 598:5                <fn>
dart-sdk/lib/async/zone.dart 1854:54                                              runBinary
dart-sdk/lib/async/future_impl.dart 239:22                                        handleError
dart-sdk/lib/async/future_impl.dart 963:46                                        handleError
dart-sdk/lib/async/future_impl.dart 984:13                                        _propagateToListeners
dart-sdk/lib/async/future_impl.dart 741:5                                         [_completeErrorObject]
dart-sdk/lib/async/future_impl.dart 745:5                                         [_completeError]
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 511:7                completeError
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 582:12               _asyncRethrow
package:just_audio_web/just_audio_web.dart 524:16                                 <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 623:19               <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 648:23               <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 594:19               <fn>
dart-sdk/lib/async/zone.dart 1849:54                                              runUnary
dart-sdk/lib/async/future_impl.dart 224:18                                        handleValue
dart-sdk/lib/async/future_impl.dart 951:44                                        handleValueCallback
dart-sdk/lib/async/future_impl.dart 980:13                                        _propagateToListeners
dart-sdk/lib/async/future_impl.dart 723:5                                         [_completeWithValue]
dart-sdk/lib/async/future_impl.dart 807:7                                         callback
dart-sdk/lib/async/schedule_microtask.dart 42:13                                  _microtaskLoop
dart-sdk/lib/async/schedule_microtask.dart 60:5                                   _startMicrotaskLoop
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 118:77  tear
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 188:69               <fn>
```

**Expected behavior**
no errors

**Desktop (please complete the following information):**
 - OS: [Windows 11]
 - Browser [chrome latest]

**Flutter SDK version**
```
E:\Support\flutter\bin\flutter.bat doctor --verbose
[√] Flutter (Channel beta, 3.33.0-0.1.pre, on Microsoft Windows [Version 10.0.26100.4061], locale en-US) [549ms]
    • Flutter version 3.33.0-0.1.pre on channel beta at E:\Support\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 678de86f0b (7 days ago), 2025-05-20 10:52:57 -0700
    • Engine revision f43bd5c131
    • Dart version 3.9.0 (build 3.9.0-100.2.beta)
    • DevTools version 2.46.0

[√] Windows Version (11 Pro 64-bit, 24H2, 2009) [1,530ms]

[√] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [4.1s]
    • Android SDK at C:\Users\ias12\AppData\Local\Android\sdk
    • Emulator version unknown
    • Platform android-36, build-tools 36.0.0
    • Java binary at: C:\Users\ias12\AppData\Local\Programs\Android Studio\jbr\bin\java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.6+-13391695-b895.109)
    • All Android licenses accepted.

[√] Chrome - develop for the web [335ms]
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.14.2) [334ms]
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.14.36121.58
    • Windows 10 SDK version 10.0.26100.0

[√] Android Studio (version 2025.1.2) [228ms]
    • Android Studio at C:\Users\ias12\AppData\Local\Programs\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.6+-13391695-b895.109)

[√] IntelliJ IDEA Community Edition (version 2025.2) [226ms]
    • IntelliJ at C:\Users\ias12\AppData\Local\Programs\IntelliJ IDEA Community Edition
    • Flutter plugin version 85.3.2
    • Dart plugin version 252.16512.17

[√] Connected device (3 available) [345ms]
    • Windows (desktop) • windows • windows-x64    • Microsoft Windows [Version 10.0.26100.4061]
    • Chrome (web)      • chrome  • web-javascript • Google Chrome 137.0.7151.41
    • Edge (web)        • edge    • web-javascript • Microsoft Edge 136.0.3240.92

[√] Network resources [1,722ms]
    • All expected network resources are available.

• No issues found!
Process finished with exit code 0
```

**Additional context**
the issue is with setAudioSources(preload: true), if replaced with preload: false and then player.load() after that it will work as normal



Discussion: ```dart
          floatingActionButton: FloatingActionButton(
            child: const Icon(Icons.label),
            onPressed: () async {
              _player.stop();
              await _player.setAudioSources(_playlist1, initialIndex: 0, initialPosition: Duration.zero, preload: true);
              //_player.load();
              _player.play();
            },
          ),
```

Why are you calling `stop()` here, and additionally, if you are calling it, why are you not awaiting it? This method unloads the plugin, which is a strange thing to do given that the very next line of code suggests you are not done with the plugin yet. If you await for `stop` before loading the plugin again, it will work (although it would be pointless to unload and load the plugin immediately). If you completely delete the `stop` call, then it will also work, more efficiently. If you want to stop playing, without unloading the plugin, use `pause` instead of `stop`.
thank you for the quick respond,

this is a sample code trying to simulate the issue that I'm facing in my app code so yes it does not make sense here

the list/plugin is changed in my code, in this sample code it is the same just for simplicity.

it seems this sample is not exactly reflecting the issue, I'm still facing it in my app even when using ``await stop()``

will update the sample again to reflect the issue soon
Yes, but if you are calling `setAudioSource` and `stop` from different event handlers, you are probably not ensuring that one handler completes before the other. In that scenario, you might need to use something like the `synchronized` package.

Fix patch:
diff --git a/just_audio/CHANGELOG.md b/just_audio/CHANGELOG.md
index 551d4baf..963a3921 100644
--- a/just_audio/CHANGELOG.md
+++ b/just_audio/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.10.4
+
+* Fix bug on simultaneous loads.
+* Fix dispose crash on iOS.
+* Fix initial seek values on subsequent loads (@Abestanis).
+
 ## 0.10.3
 
 * Fix pending timers bug in unit tests.
diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java
index 020aa17b..6c966df8 100644
--- a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java
+++ b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java
@@ -735,7 +735,7 @@ private void load(final List<MediaSource> mediaSources, ShuffleOrder shuffleOrde
         case idle:
             break;
         case loading:
-            abortExistingConnection();
+            abortExistingConnection(false);
             player.stop();
             break;
         default:
@@ -937,15 +937,17 @@ private long getDuration() {
         }
     }
 
-    private void sendError(int errorCode, String errorMsg) {
-        sendError(errorCode, errorMsg, null);
+    private void sendError(int errorCode, String errorMsg, Object details) {
+        sendError(errorCode, errorMsg, details, true);
     }
 
-    private void sendError(int errorCode, String errorMsg, Object details) {
+    private void sendError(int errorCode, String errorMsg, Object details, boolean switchToIdle) {
         eventChannel.error(String.valueOf(errorCode), errorMsg, details);
         this.errorCode = errorCode;
         this.errorMessage = errorMsg;
-        processingState = ProcessingState.idle;
+        if (switchToIdle) {
+            processingState = ProcessingState.idle;
+        }
         broadcastImmediatePlaybackEvent();
         if (prepareResult != null) {
             prepareResult.error(String.valueOf(errorCode), errorMsg, details);
@@ -1043,7 +1045,7 @@ public void seek(final long position, final Integer index, final Result result)
 
     public void dispose() {
         if (processingState == ProcessingState.loading) {
-            abortExistingConnection();
+            abortExistingConnection(true);
         }
         if (playResult != null) {
             playResult.success(new HashMap<String, Object>());
@@ -1073,8 +1075,8 @@ private void abortSeek() {
         }
     }
 
-    private void abortExistingConnection() {
-        sendError(ERROR_ABORT, "Connection aborted");
+    private void abortExistingConnection(boolean switchToIdle) {
+        sendError(ERROR_ABORT, "Connection aborted", null, switchToIdle);
     }
 
     // Dart can't distinguish between int sizes so
diff --git a/just_audio/darwin/just_audio/Sources/just_audio/AudioPlayer.m b/just_audio/darwin/just_audio/Sources/just_audio/AudioPlayer.m
index 5427a2f6..fe438115 100644
--- a/just_audio/darwin/just_audio/Sources/just_audio/AudioPlayer.m
+++ b/just_audio/darwin/just_audio/Sources/just_audio/AudioPlayer.m
@@ -608,7 +608,7 @@ - (void)load:(NSDictionary *)source initialPosition:(CMTime)initialPosition init
         [_player pause];
     }
     if (_processingState == psLoading) {
-        [self abortExistingConnection];
+        [self abortExistingConnection:NO];
     }
     _loadResult = result;
     _processingState = psLoading;
@@ -992,11 +992,11 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
 }
 
 - (void)sendErrorForItem:(IndexedPlayerItem *)playerItem {
-    [self sendError:@((int)playerItem.error.code) errorMessage:playerItem.error.localizedDescription playerItem:playerItem];
+    [self sendError:@((int)playerItem.error.code) errorMessage:playerItem.error.localizedDescription playerItem:playerItem switchToIdle:YES];
     [_player removeAllItems];
 }
 
-- (void)sendError:(NSNumber *)errorCode errorMessage:(NSString *)errorMessage playerItem:(IndexedPlayerItem *)playerItem {
+- (void)sendError:(NSNumber *)errorCode errorMessage:(NSString *)errorMessage playerItem:(IndexedPlayerItem *)playerItem switchToIdle:(BOOL)switchToIdle {
     //NSLog(@"sendError (%@) %@", errorCode, errorMessage);
     FlutterError *flutterError = [FlutterError errorWithCode:[NSString stringWithFormat:@"%@", errorCode]
                                                      message:errorMessage
@@ -1004,7 +1004,9 @@ - (void)sendError:(NSNumber *)errorCode errorMessage:(NSString *)errorMessage pl
     [_eventChannel sendEvent:flutterError];
     _errorCode = errorCode;
     _errorMessage = errorMessage;
-    _processingState = psIdle;
+    if (switchToIdle) {
+        _processingState = psIdle;
+    }
     [self broadcastPlaybackEvent];
     if (_loadResult && playerItem == _player.currentItem) {
         _loadResult(flutterError);
@@ -1012,8 +1014,8 @@ - (void)sendError:(NSNumber *)errorCode errorMessage:(NSString *)errorMessage pl
     }
 }
 
-- (void)abortExistingConnection {
-    [self sendError:@(ERROR_ABORT) errorMessage:@"Connection aborted" playerItem:nil];
+- (void)abortExistingConnection:(BOOL)switchToIdle {
+    [self sendError:@(ERROR_ABORT) errorMessage:@"Connection aborted" playerItem:nil switchToIdle:switchToIdle];
 }
 
 - (int)indexForItem:(IndexedPlayerItem *)playerItem {
diff --git a/just_audio/darwin/just_audio/Sources/just_audio/BetterEventChannel.m b/just_audio/darwin/just_audio/Sources/just_audio/BetterEventChannel.m
index d74a0ddc..3f3c90c8 100644
--- a/just_audio/darwin/just_audio/Sources/just_audio/BetterEventChannel.m
+++ b/just_audio/darwin/just_audio/Sources/just_audio/BetterEventChannel.m
@@ -32,7 +32,11 @@ - (void)sendEvent:(id)event {
 
 - (void)dispose {
     if (_eventSink) {
-        _eventSink(FlutterEndOfEventStream);
+        @try {
+            _eventSink(FlutterEndOfEventStream);
+        } @catch (NSException *exception) {
+            NSLog(@"Exception while ending event stream: %@", exception.reason);
+        }
     }
     [_eventChannel setStreamHandler:nil];
 }
diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart
index 09d830df..9ebd9a39 100644
--- a/just_audio/lib/just_audio.dart
+++ b/just_audio/lib/just_audio.dart
@@ -57,7 +57,7 @@ JustAudioPlatform get _pluginPlatform {
 /// player, including any temporary files created to cache assets.
 class AudioPlayer {
   static String _generateId() => _uuid.v4();
-  final _lock = Lock();
+  final _lock = Lock(reentrant: true);
   Future<void>? _playbackEventPipe;
 
   /// The user agent to set on all HTTP requests.
@@ -911,10 +911,7 @@ class AudioPlayer {
       return await _load(
         await _platform,
         _playlist,
-        initialSeekValues: (
-          index: pluginLoadRequest?.initialIndex ?? currentIndex,
-          position: pluginLoadRequest?.initialPosition ?? position,
-        ),
+        initialSeekValues: pluginLoadRequest?.initialSeekValues,
       );
     } else {
       // This will implicitly load the current audio source.
@@ -1023,8 +1020,8 @@ class AudioPlayer {
       checkInterruption();
       _pluginLoadRequest = null;
       return duration;
-    } on PlatformException catch (e) {
-      throw _convertException(e);
+    } on PlatformException catch (e, st) {
+      Error.throwWithStackTrace(_convertException(e), st);
     }
   }
 
@@ -1455,11 +1452,6 @@ class AudioPlayer {
       await _loopModeSubject.close();
       await _shuffleModeEnabledSubject.close();
       await _shuffleModeEnabledSubject.close();
-
-      if (playbackEvent.processingState != ProcessingState.idle) {
-        _playbackEventSubject
-            .add(playbackEvent.copyWith(processingState: ProcessingState.idle));
-      }
     });
   }
 
@@ -1493,7 +1485,8 @@ class AudioPlayer {
     /// Tells whether we've been interrupted.
     bool wasInterrupted() =>
         _activationCount != activationNumber ||
-        pluginLoadRequest != _pluginLoadRequest;
+        pluginLoadRequest != _pluginLoadRequest ||
+        _disposed;
 
     final durationCompleter = Completer<Duration?>();
 
@@ -1606,52 +1599,60 @@ class AudioPlayer {
       // getting crossed, although it would be better to find a way to flush the
       // event channel and keep the same ID.
 
-      // Strangely, this throws "Cannot complete a future with itself" when
-      // _playbackEventSubscription==null under flutter test.
-      // await _playbackEventSubscription?.cancel();
-      // await _playerDataSubscription?.cancel();
-      if (_playbackEventSubscription != null) {
-        await _playbackEventSubscription!.cancel();
-      }
-      if (_playerDataSubscription != null) {
-        await _playerDataSubscription!.cancel();
+      AudioPlayerPlatform inactiveResult(AudioPlayerPlatform platform) {
+        durationCompleter.complete(null);
+        return platform;
       }
 
-      if (!force) {
-        final oldPlatform = _platformValue!;
-        if (oldPlatform is! _IdleAudioPlayer) {
-          await _disposePlatform(oldPlatform);
+      final platform = await _lock.synchronized(() async {
+        final oldPlatform = _platformValue;
+        // Strangely, this throws "Cannot complete a future with itself" when
+        // _playbackEventSubscription==null under flutter test.
+        // await _playbackEventSubscription?.cancel();
+        // await _playerDataSubscription?.cancel();
+        if (_playbackEventSubscription != null) {
+          await _playbackEventSubscription!.cancel();
+        }
+        if (_playerDataSubscription != null) {
+          await _playerDataSubscription!.cancel();
         }
-      }
-      if (_disposed) return _platform;
-      // During initialisation, we must only use this platform reference in case
-      // _platform is updated again during initialisation.
-      final platform = active
-          ? await (_nativePlatform = _pluginPlatform.init(InitRequest(
-              id: _id = _generateId(),
-              audioLoadConfiguration: _audioLoadConfiguration?._toMessage(),
-              androidAudioEffects: (_isAndroid() || _isUnitTest())
-                  ? _audioPipeline.androidAudioEffects
-                      .map((audioEffect) => audioEffect._toMessage())
-                      .toList()
-                  : [],
-              darwinAudioEffects: (_isDarwin() || _isUnitTest())
-                  ? _audioPipeline.darwinAudioEffects
-                      .map((audioEffect) => audioEffect._toMessage())
-                      .toList()
-                  : [],
-              androidOffloadSchedulingEnabled: _androidOffloadSchedulingEnabled,
-              useLazyPreparation: _playlist.useLazyPreparation,
-            )))
-          : (_idlePlatform = _IdleAudioPlayer(
-              id: _id = _generateId(),
-              sequenceStream: sequenceStream,
-              errorCode: playbackEvent.errorCode,
-              errorMessage: playbackEvent.errorMessage,
-            ));
-      if (checkInterruption()) return platform;
 
-      _platformValue = platform;
+        if (!force) {
+          if (oldPlatform != null && oldPlatform is! _IdleAudioPlayer) {
+            await _disposePlatform(oldPlatform);
+          }
+        }
+        // During initialisation, we must only use this platform reference in case
+        // _platform is updated again during initialisation.
+        final platform = active && !_disposed
+            ? await (_nativePlatform = _pluginPlatform.init(InitRequest(
+                id: _id = _generateId(),
+                audioLoadConfiguration: _audioLoadConfiguration?._toMessage(),
+                androidAudioEffects: (_isAndroid() || _isUnitTest())
+                    ? _audioPipeline.androidAudioEffects
+                        .map((audioEffect) => audioEffect._toMessage())
+                        .toList()
+                    : [],
+                darwinAudioEffects: (_isDarwin() || _isUnitTest())
+                    ? _audioPipeline.darwinAudioEffects
+                        .map((audioEffect) => audioEffect._toMessage())
+                        .toList()
+                    : [],
+                androidOffloadSchedulingEnabled:
+                    _androidOffloadSchedulingEnabled,
+                useLazyPreparation: _playlist.useLazyPreparation,
+              )))
+            : (_idlePlatform = _IdleAudioPlayer(
+                id: _id = _generateId(),
+                sequenceStream: sequenceStream,
+                errorCode: playbackEvent.errorCode,
+                errorMessage: playbackEvent.errorMessage,
+              ));
+
+        _platformValue = platform;
+        return platform;
+      });
+      if (checkInterruption() || _disposed) return inactiveResult(platform);
 
       if (active) {
         if (playlist.children.isNotEmpty) {
@@ -1671,14 +1672,14 @@ class AudioPlayer {
         if (_isAndroid() || _isUnitTest()) {
           if (_androidApplyAudioAttributes) {
             final audioSession = await AudioSession.instance;
-            if (checkInterruption()) return platform;
+            if (checkInterruption()) return inactiveResult(platform);
             _androidAudioAttributes ??=
                 audioSession.configuration?.androidAudioAttributes;
           }
           if (_androidAudioAttributes != null) {
             await _internalSetAndroidAudioAttributes(
                 platform, _androidAudioAttributes!);
-            if (checkInterruption()) return platform;
+            if (checkInterruption()) return inactiveResult(platform);
           }
         }
         if (!automaticallyWaitsToMinimizeStalling) {
@@ -1686,50 +1687,50 @@ class AudioPlayer {
           await platform.setAutomaticallyWaitsToMinimizeStalling(
               SetAutomaticallyWaitsToMinimizeStallingRequest(
                   enabled: automaticallyWaitsToMinimizeStalling));
-          if (checkInterruption()) return platform;
+          if (checkInterruption()) return inactiveResult(platform);
         }
         await platform.setVolume(SetVolumeRequest(volume: volume));
-        if (checkInterruption()) return platform;
+        if (checkInterruption()) return inactiveResult(platform);
         await platform.setSpeed(SetSpeedRequest(speed: speed));
-        if (checkInterruption()) return platform;
+        if (checkInterruption()) return inactiveResult(platform);
         try {
           await platform.setPitch(SetPitchRequest(pitch: pitch));
         } catch (e) {
           // setPitch not supported on this platform.
         }
-        if (checkInterruption()) return platform;
+        if (checkInterruption()) return inactiveResult(platform);
         try {
           await platform.setSkipSilence(
               SetSkipSilenceRequest(enabled: skipSilenceEnabled));
         } catch (e) {
           // setSkipSilence not supported on this platform.
         }
-        if (checkInterruption()) return platform;
+        if (checkInterruption()) return inactiveResult(platform);
         await platform.setLoopMode(SetLoopModeRequest(
             loopMode: LoopModeMessage.values[loopMode.index]));
-        if (checkInterruption()) return platform;
+        if (checkInterruption()) return inactiveResult(platform);
         await platform.setShuffleMode(SetShuffleModeRequest(
             shuffleMode: shuffleModeEnabled
                 ? ShuffleModeMessage.all
                 : ShuffleModeMessage.none));
-        if (checkInterruption()) return platform;
+        if (checkInterruption()) return inactiveResult(platform);
         if (kIsWeb) {
           if (_webCrossOrigin != null) {
             await platform.setWebCrossOrigin(SetWebCrossOriginRequest(
               crossOrigin: WebCrossOriginMessage.values[_webCrossOrigin!.index],
             ));
-            if (checkInterruption()) return platform;
+            if (checkInterruption()) return inactiveResult(platform);
           }
           if (_webSinkId != '') {
             await platform.setWebSinkId(SetWebSinkIdRequest(
               sinkId: _webSinkId,
             ));
-            if (checkInterruption()) return platform;
+            if (checkInterruption()) return inactiveResult(platform);
           }
         }
         for (var audioEffect in _audioPipeline._audioEffects) {
           await audioEffect._activate(platform);
-          if (checkInterruption()) return platform;
+          if (checkInterruption()) return inactiveResult(platform);
         }
         if (playing) {
           _sendPlayRequest(platform, playCompleter);
diff --git a/just_audio/pubspec.yaml b/just_audio/pubspec.yaml
index ced04a69..1077b672 100644
--- a/just_audio/pubspec.yaml
+++ b/just_audio/pubspec.yaml
@@ -1,6 +1,6 @@
 name: just_audio
 description: A feature-rich audio player for Flutter. Loop, clip and sequence any sound from any source (asset/file/URL/stream) in gapless playlists.
-version: 0.10.3
+version: 0.10.4
 repository: https://github.com/ryanheise/just_audio/tree/minor/just_audio
 issue_tracker: https://github.com/ryanheise/just_audio/issues
 topics:


Test patch:
diff --git a/just_audio/test/just_audio_test.dart b/just_audio/test/just_audio_test.dart
index 681b1082..ceaeef0e 100644
--- a/just_audio/test/just_audio_test.dart
+++ b/just_audio/test/just_audio_test.dart
@@ -176,6 +176,28 @@ void runTests() {
     await player.dispose();
   });
 
+  test('initial-seek-values', () async {
+    final player = AudioPlayer();
+    const targetPos = Duration(seconds: 2);
+    await player.setAudioSources([
+      AudioSource.uri(Uri.parse('https://a.a/a.mp3')),
+      AudioSource.uri(Uri.parse('https://b.b/b.mp3')),
+    ], initialPosition: targetPos, initialIndex: 1);
+    expect(player.position, targetPos);
+    expect(player.currentIndex, 1);
+    await player.stop();
+    await player.load();
+    expect(player.position, targetPos);
+    expect(player.currentIndex, 1);
+    await player.setAudioSources([
+      AudioSource.uri(Uri.parse('https://a.a/a.mp3')),
+      AudioSource.uri(Uri.parse('https://b.b/b.mp3')),
+    ]);
+    expect(player.position, equals(Duration.zero));
+    expect(player.currentIndex, 0);
+    await player.dispose();
+  });
+
   test('load error', () async {
     final player = AudioPlayer();
     Object? exception;
@@ -1204,6 +1226,41 @@ void runTests() {
     await player.dispose();
   });
 
+  test('load-stop-load-simultaneous', () async {
+    final player = AudioPlayer();
+    await player
+        .setAudioSource(AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')));
+    final stop1Future = player.stop();
+    final load1Future = player
+        .setAudioSource(AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')));
+    expectLater(load1Future, completion(audioSourceDuration));
+    await stop1Future.catchError((e) => null);
+    await load1Future.catchError((e) => null);
+    await player.dispose();
+  });
+
+  test('load-simultaneous', () async {
+    final player = AudioPlayer();
+    // Activate the platform
+    await player
+        .setAudioSource(AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')));
+    // Do two simultaneous loads
+    mock.mostRecentPlayer?.blockLoad();
+    final load1Future = player
+        .setAudioSource(AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')));
+    // Wait for first load to enter platform before starting second load.
+    await player.processingStateStream
+        .firstWhere((s) => s == ProcessingState.loading);
+    final load2Future = player
+        .setAudioSource(AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')));
+    mock.mostRecentPlayer?.unblockLoad();
+    expectLater(() => load1Future, throwsException);
+    expectLater(load2Future, completion(audioSourceDuration));
+    await load1Future.catchError((e) => null);
+    await load2Future.catchError((e) => null);
+    await player.dispose();
+  });
+
   test('load-load', () async {
     final player = AudioPlayer();
     await player.setAudioSource(
@@ -1739,6 +1796,7 @@ class MockAudioPlayer extends AudioPlayerPlatform {
   ProcessingStateMessage _processingState = ProcessingStateMessage.idle;
   Duration _updatePosition = Duration.zero;
   DateTime _updateTime = DateTime.now();
+  MockLoadRequest? _loadRequest;
   // ignore: prefer_final_fields
   Duration? _duration;
   int? _index;
@@ -1747,6 +1805,9 @@ class MockAudioPlayer extends AudioPlayerPlatform {
   Completer<dynamic>? _playCompleter;
   Timer? _playTimer;
   LoopModeMessage _loopMode = LoopModeMessage.off;
+  int? _errorCode;
+  String? _errorMessage;
+  Completer<void>? _loadBlock;
 
   MockAudioPlayer(InitRequest request)
       : audioLoadConfiguration = request.audioLoadConfiguration,
@@ -1764,23 +1825,54 @@ class MockAudioPlayer extends AudioPlayerPlatform {
     dataMessageController.add(message);
   }
 
+  void blockLoad() {
+    _loadBlock ??= Completer();
+  }
+
+  void unblockLoad() {
+    _loadBlock?.complete();
+    _loadBlock = null;
+  }
+
+  Future<void> _loadReady() async {
+    if (_loadBlock != null) {
+      await _loadBlock!.future;
+    }
+  }
+
+  PlatformException _sendError(int errorCode, String errorMessage,
+      {bool switchToIdle = true}) {
+    final e = PlatformException(code: '$errorCode', message: errorMessage);
+    eventController.addError(e);
+    if (switchToIdle) {
+      _processingState = ProcessingStateMessage.idle;
+    }
+    _errorCode = errorCode;
+    _errorMessage = errorMessage;
+    _broadcastPlaybackEvent();
+    return e;
+  }
+
   @override
   Future<LoadResponse> load(LoadRequest request) async {
+    _loadRequest?.interrupted = true;
+    final loadRequest = _loadRequest = MockLoadRequest();
     final playlist =
         request.audioSourceMessage as ConcatenatingAudioSourceMessage;
     if (playlist.children.isEmpty) return LoadResponse(duration: null);
     final audioSource = playlist.children.first;
     _processingState = ProcessingStateMessage.loading;
+    _errorCode = null;
+    _errorMessage = null;
     _broadcastPlaybackEvent();
     if (audioSource is UriAudioSourceMessage) {
       final uri = Uri.parse(audioSource.uri);
       if (uri.path.contains('abort')) {
-        throw PlatformException(code: 'abort', message: 'Failed to load URL');
+        throw _sendError(415, 'Failed to load URL');
       } else if (uri.path.contains('404')) {
-        throw PlatformException(
-            code: '404', message: 'Not found: ${audioSource.uri}');
+        throw _sendError(404, 'Not found: ${audioSource.uri}');
       } else if (uri.path.contains('error')) {
-        throw PlatformException(code: 'error', message: 'Unknown error');
+        throw _sendError(500, 'Unknown error');
       }
       _duration = audioSourceDuration;
     } else if (audioSource is ClippingAudioSourceMessage) {
@@ -1794,13 +1886,17 @@ class MockAudioPlayer extends AudioPlayerPlatform {
     _audioSource = audioSource;
     _index = request.initialIndex ?? 0;
     // Simulate loading time.
-    await Future<void>.delayed(const Duration(milliseconds: 50));
+    await _loadReady();
+    if (loadRequest.interrupted) {
+      throw _sendError(10000000, 'Connection aborted', switchToIdle: false);
+    }
     _setPosition(request.initialPosition ?? Duration.zero);
     _processingState = ProcessingStateMessage.ready;
     _broadcastPlaybackEvent();
     if (_playing) {
       _startTimer();
     }
+    _loadRequest = null;
     return LoadResponse(duration: _duration);
   }
 
@@ -1820,6 +1916,8 @@ class MockAudioPlayer extends AudioPlayerPlatform {
     _playTimer = Timer(_remaining, () {
       _setPosition(_position);
       _processingState = ProcessingStateMessage.completed;
+      _errorCode = null;
+      _errorMessage = null;
       _broadcastPlaybackEvent();
       _playCompleter?.complete();
     });
@@ -1908,8 +2006,12 @@ class MockAudioPlayer extends AudioPlayerPlatform {
 
   @override
   Future<DisposeResponse> dispose(DisposeRequest request) async {
+    await Future<dynamic>.delayed(const Duration(milliseconds: 10));
     _processingState = ProcessingStateMessage.idle;
+    _errorCode = null;
+    _errorMessage = null;
     _broadcastPlaybackEvent();
+    eventController.close();
     return DisposeResponse();
   }
 
@@ -1942,6 +2044,8 @@ class MockAudioPlayer extends AudioPlayerPlatform {
     }
     eventController.add(PlaybackEventMessage(
       processingState: _processingState,
+      errorCode: _errorCode,
+      errorMessage: _errorMessage,
       updatePosition: _updatePosition,
       updateTime: _updateTime,
       bufferedPosition: _position,
@@ -2043,6 +2147,10 @@ class MockAudioPlayer extends AudioPlayerPlatform {
   }
 }
 
+class MockLoadRequest {
+  bool interrupted = false;
+}
+
 final byteRangeData = List.generate(200, (i) => i);
 
 class TestStreamAudioSource extends StreamAudioSource {