Skip to content

React Native in-memory storage fallback can never run #205

Open

Description

@ejsmith

ReactNativeExceptionlessClient.configureStorage in @exceptionless/react-native 3.2.1 wraps storage setup in a try/catch that is meant to fall back to in-memory storage when AsyncStorage is unavailable. Nothing inside the try can throw, and the real failure happens earlier, when the module is imported, so the fallback is dead code.

Where

What happens

  • If the AsyncStorage native module isn't linked (for example, pod install wasn't run or the app wasn't rebuilt), @react-native-async-storage/async-storage throws [@RNC/AsyncStorage]: NativeModule: AsyncStorage is null at module evaluation time (src/AsyncStorage.native.ts, line 22, in 2.2.0). Importing @exceptionless/react-native throws before startup() runs, so the app fails instead of falling back.
  • If storage calls fail later, the AsyncStorageProvider methods return rejected promises from inside the queue. The catch in configureStorage is never involved.
  • The log message "AsyncStorage provider failed to initialize. Using in-memory storage." is never written.

Expected

If AsyncStorage can't be loaded or used, the client logs the message and keeps working with the default in-memory storage and usePersistedQueueStorage = false, as the catch intends.

Steps to reproduce

  1. Install @exceptionless/react-native 3.2.1 and @react-native-async-storage/async-storage in a bare React Native iOS app, but skip pod install (or run a build that doesn't include the AsyncStorage native module).
  2. Import Exceptionless from @exceptionless/react-native and call startup.
  3. The app throws NativeModule: AsyncStorage is null while importing the package. The fallback message is never logged.

Suggested fix

Load AsyncStorage lazily inside the guarded path, for example await import("@react-native-async-storage/async-storage") (or a require) in configureStorage, made async and awaited from startup. Then verify it works with an awaited call such as getAllKeys() before setting usePersistedQueueStorage = true. If the fallback is supported, mark the peer dependency optional in peerDependenciesMeta. Otherwise, remove the try/catch and the message.

Activity

  1. dave-otf commented on Oct 30, 2026

    @dave-otf

    One edge case worth covering alongside the missing-native-module case: lazy import moves the import-time failure into the try/catch, but a storage method can still reject later. If the intended contract is “fall back whenever persistence is unavailable,” await a small probe such as getAllKeys() inside the guarded setup before enabling usePersistedQueueStorage, and test both a module-load throw and a rejected probe. If only a missing native module should fall back, making the peer dependency optional would make that narrower contract clear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions