Fix flakiness is mac integration tests (#9990)
diff --git a/packages/devtools_app_shared/CHANGELOG.md b/packages/devtools_app_shared/CHANGELOG.md
index 7dadf83..83756df 100644
--- a/packages/devtools_app_shared/CHANGELOG.md
+++ b/packages/devtools_app_shared/CHANGELOG.md
@@ -7,6 +7,7 @@
 * Fix a `RangeError` thrown by `SplitPane` when the number of children
   changes between rebuilds.
 * Fix garbage collection issues with the result list in `asyncEval` on both native VM and web.
+* Safely handle RPC errors and unexpected exceptions when calling service extensions in `ServiceExtensionManager`.
 * The minimum Dart SDK version is bumped to 3.11.0.
 * The minimum Flutter SDK version is bumped to 3.41.0.
 
diff --git a/packages/devtools_app_shared/lib/src/service/service_extension_manager.dart b/packages/devtools_app_shared/lib/src/service/service_extension_manager.dart
index a76f8b0..c2d9baf 100644
--- a/packages/devtools_app_shared/lib/src/service/service_extension_manager.dart
+++ b/packages/devtools_app_shared/lib/src/service/service_extension_manager.dart
@@ -222,12 +222,21 @@
       if (didSendFirstFrameEvent) {
         await _onFrameEventReceived();
       }
+    } on SentinelException catch (_) {
+      // Service extension or isolate stopped existing while calling, so do
+      // nothing. This typically happens during hot restarts.
+      return;
     } on RPCError catch (e) {
-      if (e.code == RPCErrorKind.kServerError.code) {
+      if (e.code == RPCErrorKind.kServerError.code ||
+          e.isServiceDisposedError) {
         // Connection disappeared
         return;
       }
-      rethrow;
+      _log.warning('Error checking for first Flutter frame: $e');
+      return;
+    } catch (e, st) {
+      _log.warning('Error checking for first Flutter frame: $e', e, st);
+      return;
     }
   }
 
@@ -274,6 +283,8 @@
       } on SentinelException catch (_) {
         // Service extension stopped existing while calling, so do nothing.
         // This typically happens during hot restarts.
+      } catch (e, st) {
+        _log.warning('Error adding service extension $name: $e', e, st);
       }
     } else {
       // Set any extensions that are already enabled on the device. This will
@@ -338,6 +349,10 @@
             );
             await _maybeRestoreExtension(name, value);
         }
+      } on SentinelException catch (_) {
+        // Service extension or isolate stopped existing while restoring, so do
+        // nothing. This typically happens during hot restarts.
+        return false;
       } on RPCError catch (e) {
         if (e.isServiceDisposedError) {
           return false;
@@ -438,12 +453,21 @@
             args: {name.substring(name.lastIndexOf('.') + 1): value},
           );
         }
+      } on SentinelException catch (_) {
+        // Service extension or isolate stopped existing while calling, so do
+        // nothing. This typically happens during hot restarts.
+        return false;
       } on RPCError catch (e) {
-        if (e.code == RPCErrorKind.kServerError.code) {
+        if (e.isServiceDisposedError ||
+            e.code == RPCErrorKind.kServerError.code) {
           // The connection disappeared.
           return false;
         }
-        rethrow;
+        _log.warning('Failed to call service extension $name: $e');
+        return false;
+      } catch (e, st) {
+        _log.warning('Failed to call service extension $name: $e', e, st);
+        return false;
       }
 
       return true;
diff --git a/packages/devtools_app_shared/test/service/service_extensions_test.dart b/packages/devtools_app_shared/test/service/service_extensions_test.dart
index 2ee39d2..c1391d6 100644
--- a/packages/devtools_app_shared/test/service/service_extensions_test.dart
+++ b/packages/devtools_app_shared/test/service/service_extensions_test.dart
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
 
+import 'package:devtools_app_shared/service.dart';
 import 'package:devtools_app_shared/service_extensions.dart';
-import 'package:test/test.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:vm_service/vm_service.dart';
 
 void main() {
   group('ServiceExtensions', () {
@@ -28,5 +30,95 @@
         isTrue,
       );
     });
+
+    test(
+      'ServiceExtensionManager handles RPCError / DWDS Promise collected gracefully',
+      () async {
+        var extensionCalls = 0;
+        final fakeService = _FakeVmService(
+          onCallServiceExtension: (method, isolateId, args) {
+            extensionCalls++;
+            throw RPCError(
+              'callServiceExtension',
+              -32603,
+              'Unexpected DWDS error for callServiceExtension: WipError\n-32000 Promise was collected',
+            );
+          },
+        );
+
+        final isolateManager = IsolateManager();
+        isolateManager.vmServiceOpened(fakeService);
+
+        final manager = ServiceExtensionManager(isolateManager);
+        manager.vmServiceOpened(fakeService, _FakeConnectedApp());
+
+        // Pre-enable an extension (like ErrorBadgeManager does for structuredErrors).
+        await manager.setServiceExtensionState(
+          structuredErrors.extension,
+          enabled: true,
+          value: true,
+          callExtension: false,
+        );
+
+        // Initialize isolates on IsolateManager.
+        final isolateRef = IsolateRef(
+          id: 'isolate-1',
+          number: '1',
+          name: 'main',
+        );
+        await isolateManager.init([isolateRef]);
+
+        // Wait for all async listeners and microtasks to complete.
+        await pumpEventQueue();
+
+        // Verify that the manager attempted to restore the pre-enabled extension
+        // state on the newly registered isolate (and gracefully handled the error).
+        expect(extensionCalls, equals(1));
+      },
+    );
   });
 }
+
+/// A fake [VmService] implementation for testing service extension calls.
+class _FakeVmService extends Fake implements VmService {
+  _FakeVmService({required this.onCallServiceExtension});
+
+  final Future<Response> Function(
+    String method,
+    String? isolateId,
+    Map<String, dynamic>? args,
+  )
+  onCallServiceExtension;
+
+  @override
+  Stream<Event> get onIsolateEvent => const Stream<Event>.empty();
+
+  @override
+  Stream<Event> get onExtensionEvent => const Stream<Event>.empty();
+
+  @override
+  Stream<Event> get onDebugEvent => const Stream<Event>.empty();
+
+  @override
+  Future<Isolate> getIsolate(String isolateId) async => Isolate(
+    id: isolateId,
+    number: '1',
+    name: 'main',
+    extensionRPCs: [structuredErrors.extension],
+  );
+
+  @override
+  Future<Response> callServiceExtension(
+    String method, {
+    String? isolateId,
+    Map<String, dynamic>? args,
+  }) {
+    return onCallServiceExtension(method, isolateId, args);
+  }
+}
+
+/// A fake [ConnectedApp] implementation that simulates a connected Flutter app.
+class _FakeConnectedApp extends Fake implements ConnectedApp {
+  @override
+  Future<bool> get isFlutterApp async => true;
+}