Repository navigation
React Native in-memory storage fallback can never run #205
Description
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
packages/react-native/src/ReactNativeExceptionlessClient.ts#L32-L40: thetryonly constructsAsyncStorageProvider(no constructor) and assigns two plain propertiespackages/react-native/src/storage/AsyncStorageProvider.ts#L1-L2:@react-native-async-storage/async-storageis a static top-level importpackages/react-native/package.json#L52-L56: AsyncStorage is a required peer dependency
What happens
- If the AsyncStorage native module isn't linked (for example,
pod installwasn't run or the app wasn't rebuilt),@react-native-async-storage/async-storagethrows[@RNC/AsyncStorage]: NativeModule: AsyncStorage is nullat module evaluation time (src/AsyncStorage.native.ts, line 22, in 2.2.0). Importing@exceptionless/react-nativethrows beforestartup()runs, so the app fails instead of falling back. - If storage calls fail later, the
AsyncStorageProvidermethods return rejected promises from inside the queue. ThecatchinconfigureStorageis 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
- Install
@exceptionless/react-native3.2.1 and@react-native-async-storage/async-storagein a bare React Native iOS app, but skippod install(or run a build that doesn't include the AsyncStorage native module). - Import
Exceptionlessfrom@exceptionless/react-nativeand callstartup. - The app throws
NativeModule: AsyncStorage is nullwhile 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
dave-otf commentedon Oct 30, 2026
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.