Skip to content

[Bug]: iOS fatal crash — mutex lock failed: Invalid argument in MapboxCommon wss_backend::Service::write #4250

Description

@phongnguyen-star

Mapbox Implementation

Mapbox

Mapbox Version

MapboxMaps 11.15.4 / MapboxCommon 24.15.4 (via CocoaPods / @rnmapbox/maps default for 10.2.0)

React Native Version

0.81.1 (New Architecture enabled)

Platform

iOS

@rnmapbox/maps version

10.2.0

Standalone component to reproduce

Crash is intermittent in production (Firebase Crashlytics) and not reliably reproducible in a minimal app yet. Map usage on Home / Device / Estimation screens follows this pattern; access token is set at splash/login via Mapbox.setAccessToken(...).

Crashlytics often reports a non-map screen as the current screen (e.g. a notes list), which suggests MapboxCommon networking continues after the map UI is no longer foregrounded.

import React, { useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import Mapbox, { Camera, StyleURL, UserTrackingMode } from '@rnmapbox/maps';

Mapbox.setAccessToken('YOUR_MAPBOX_ACCESS_TOKEN');

export default function MapCrashExample() {
  const refMapView = useRef(null);
  const refCamera = useRef(null);

  return (
    <View style={styles.container}>
      <Mapbox.MapView
        ref={refMapView}
        onDidFinishLoadingMap={() => {}}
        onPress={() => {}}
        logoEnabled={false}
        attributionEnabled={false}
        scaleBarEnabled={false}
        styleURL={StyleURL.Street}
        style={styles.map}>
        <Camera
          ref={refCamera}
          followUserMode={UserTrackingMode.Follow}
          animationMode="moveTo"
          zoomLevel={8}
        />
      </Mapbox.MapView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  map: { flex: 1 },
});

Observed behavior and steps to reproduce

Fatal Exception: mutex lock failed: Invalid argument

Throw site is inside MapboxCommon on a GCD worker queue while writing on the experimental WSS backend. The process then terminates; Crashlytics / other SDKs only appear on the terminate path.

Primary (throwing) stack

Fatal Exception: mutex lock failed: Invalid argument
0 CoreFoundation __exceptionPreprocess
1 libobjc.A.dylib objc_exception_throw
2 CoreFoundation -[NSException initWithCoder:]
3 MapboxCommon mapbox::bindgen::mapbox::common::experimental::wss_backend::Service::write(unsigned long long, std::__1::variant<std::__1::vector<unsigned char, ...>, std::__1::basic_string<char, ...>>&&) + 4000
4 libdispatch.dylib _dispatch_call_block_and_release
5 libdispatch.dylib _dispatch_client_callout
6 libdispatch.dylib _dispatch_lane_serial_drain
7 libdispatch.dylib _dispatch_lane_invoke
8 libdispatch.dylib _dispatch_root_queue_drain_deferred_wlh
9 libdispatch.dylib _dispatch_workloop_worker_thread
10 libsystem_pthread.dylib _pthread_wqthread
11 libsystem_pthread.dylib start_wqthread


### Terminate / Crashlytics recording path (secondary)

Crashed: com.google.firebase.crashlytics.ios.exception
...
FIRCLSTerminateHandler
→ Intercom / StripeTerminal terminate hooks
→ libc++abi std::__terminate
→ objc_exception_throw
→ MapboxCommon mapbox::bindgen::mapbox::common::experimental::wss_backend::Service::write(...)
→ libdispatch (same GCD worker path as above)


**Notes:**
- No app JS frames in the throwing stack.
- No Reanimated / Fabric `ShadowNode` / `nativePropsMutex` frames (this is **not** the known Reanimated ShadowTree race).
- Exception text matches classic C++ `std::system_error` when locking a destroyed/uninitialized mutex (use-after-teardown).

Suspected triggers (unconfirmed): leave a map screen, background the app, style/token lifecycle, or MapboxCommon WSS still writing after teardown.

## Expected behavior

MapboxCommon WSS / networking should not throw a fatal `mutex lock failed: Invalid argument` after map unmount, navigation away from map screens, or app backgrounding.

## Notes / preliminary analysis

1. Searched **rnmapbox/maps** and **mapbox/mapbox-maps-ios** for `"mutex lock failed"`, `wss_backend`, and `Service::write` — **no existing public issue** with this exact frame.
2. Closest historical analogues (different symbols, same teardown/mutex class):
   - [mapbox/mapbox-maps-ios#984](https://github.com/mapbox/mapbox-maps-ios/issues/984) — mutex on terminate (`Settings::get` / RunLoop); fixed in Maps 10.3
   - [mapbox/mapbox-maps-ios#2174](https://github.com/mapbox/mapbox-maps-ios/issues/2174) — `CleanupManager::cleanup()` on terminate/background (still open)
3. Because the throw is inside the **MapboxCommon binary**, `@rnmapbox/maps` cannot patch `Service::write` directly; likely needs an upstream Maps/Common bump or Mapbox-internal fix. Filing here so maintainers can track / bump default native SDK and/or forward to Mapbox.
4. We are on `@rnmapbox/maps@10.2.0`; newer patch releases exist (e.g. 10.2.10). Happy to try a Maps/Common bump if you recommend a specific `$RNMapboxMapsVersion` / package version for v11.

## Additional links and references

- Native pods: `MapboxMaps (11.15.4)`, `MapboxCommon (24.15.4)`, `rnmapbox-maps (10.2.0)`
- Related (different bug): [rnmapbox/maps#3700](https://github.com/rnmapbox/maps/issues/3700) MapboxCommon location sharedRunLoop CPU (v10 Maps pin) — not this stack

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions