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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions