-
Notifications
You must be signed in to change notification settings - Fork 237
Description
Bug description
posthog-react-native@4.36.0 throws Error: Method writeAsStringAsync imported from "expo-file-system" is deprecated on every storage write when used with Expo SDK 54 (stable).
This is distinct from #2229 (SDK 54 beta), which was fixed in #2234. The fix checks whether readAsStringAsync exists on the expo-file-system module to decide between the legacy and new File API paths:
// native-deps.js (simplified)
if (expoFileSystemLegacy.readAsStringAsync) {
return buildLegacyStorage(_filesystem); // uses writeAsStringAsync
}
// else: use new File(uri) / file.write(value)In SDK 54 beta, the legacy methods were completely removed (undefined), so this check correctly fell through to the new API.
In SDK 54 stable, the legacy methods are still exported as functions — they just throw a deprecation error when called (source). So the existence check passes, PostHog enters the legacy path, and writeAsStringAsync throws.
The require('expo-file-system/legacy') fallback (Step 1 in the detection logic) also doesn't resolve at runtime — the package ships legacy.ts but no legacy.js at the root, and PostHog's compiled JS can't resolve the TypeScript file.
How to reproduce
- Create an Expo SDK 54 app (stable, not beta)
- Install
posthog-react-native@4.36.0 - Set up PostHogProvider with default options (no
customStorage) - Run the app — errors appear on every
capture(),identify(), or any operation that persists to storage
Workaround
Pass a custom storage provider to bypass the detection logic entirely:
import { MMKV } from 'react-native-mmkv';
const storage = new MMKV({ id: 'posthog' });
<PostHogProvider
apiKey="..."
options={{
customStorage: {
getItem: (key) => storage.getString(key) ?? null,
setItem: (key, value) => storage.set(key, value),
},
}}
>Suggested fix
The detection should test whether the function actually works, not just whether it exists. For example:
try {
// Try calling readAsStringAsync with a non-existent path
// If it throws with the deprecation message, fall through to new API
await _filesystem.readAsStringAsync('__posthog_probe__');
} catch (e) {
if (e.message?.includes('deprecated')) {
// Functions exist but throw — use new File API
return buildNewFileStorage(_filesystem);
}
// Other error (e.g., file not found) — legacy API works, use it
return buildLegacyStorage(_filesystem);
}Or more simply, check _filesystem.Paths to detect the new API:
if (_filesystem.Paths && _filesystem.File) {
return buildNewFileStorage(_filesystem);
}
if (_filesystem.readAsStringAsync) {
return buildLegacyStorage(_filesystem);
}Related sub-libraries
- All of them
- posthog-js (web)
- posthog-js-lite (web lite)
- posthog-node
- posthog-react-native
- @posthog/react
- @posthog/ai
- @posthog/nextjs-config
Additional context
- Upgrading to Expo SDK-54 (beta) breaks posthog #2229 — original SDK 54 beta report (fixed in fix: expo-43 and new expo-file-system APIs with back compatibility support #2234)
- Expo File System error in Expo 54 #2453 — same bug report, closed as "already fixed" but the fix doesn't handle stable SDK 54 behavior
expo-file-systemv19.0.21 (Expo SDK 54 stable)- Confirmed on
posthog-react-native4.36.0 (latest as of Feb 2026)