ある朝、いつものように Rork で生成したアプリを起動したら、見慣れない赤い画面が出ていました。メッセージはこうです。
TurboModuleRegistry.getEnforcing(...): 'RNSomeModule' could not be found.
Verify that a module by this name is registered in the native binary.
JavaScript 側のコードは何も変えていないのに、です。原因を辿っていくと、Expo SDK 53 以降で New Architecture(Fabric / TurboModules)が標準で有効になったことが引き金でした。同じエラーで手が止まっている方は少なくないと思います。2014年から個人開発を続け、累計5,000万ダウンロードに育った壁紙・癒し系アプリを運用してきましたが、このエラーに本気で向き合ったのは New Architecture が標準化されてからでした。6本の壁紙アプリを移行したときに整理した切り分け手順を、順を追って共有します。
このエラーが言っていること
getEnforcing は「この名前のネイティブモジュールが必ず存在するはずだから探してこい」という命令です。get なら見つからなくても null を返しますが、getEnforcing は見つからなければ即座に例外を投げます。
つまりこのエラーは「JavaScript はモジュール RNSomeModule を呼ぼうとした。でも今動いているネイティブバイナリの中に、その名前で登録されたモジュールが無かった」という状態を表しています。ポイントは、問題が JS ではなくネイティブバイナリ側にあるという点です。ここを取り違えると、いくら JS を直しても直りません。
原因は、経験上ほぼ次の4つに収束します。
原因1: Expo Go で動かしている
いちばん多いのがこれです。Expo Go は、あらかじめ決められたネイティブモジュールだけを内蔵したアプリです。あなたが追加したライブラリ(例: react-native-mmkv、課金系、独自のネイティブモジュール)は Expo Go の中には入っていません。
確認は単純で、起動方法を見てください。
# これは Expo Go 経由 → カスタムネイティブモジュールは動かない
npx expo start
# これが必要 → 自分のネイティブモジュールを含む開発ビルドを使う
npx expo start --dev-client対処は、開発ビルド(development build)を作ることです。
# expo-dev-client を追加
npx expo install expo-dev-client
# 端末で動く開発ビルドを生成(クラウドビルドの例)
eas build --profile development --platform ios開発ビルドは「あなたのライブラリ構成で焼き直した Expo Go」だと考えると分かりやすいです。一度入れてしまえば、以降の JS 変更はこれまで通りホットリロードで反映されます。
原因2: ネイティブを再ビルドせずに JS だけ更新した
ネイティブモジュールを含むライブラリを npm install した直後によく起きます。RNSomeModule を提供する新しいライブラリを入れても、今端末に載っているバイナリは古いままなので、新モジュールが登録されていません。JS のバンドルだけが新しくなり、ネイティブが追いついていない状態です。
ネイティブの再ビルドが必要です。
# ネイティブプロジェクトを作り直してから
npx expo prebuild --clean
# iOS は Pods を入れ直す
cd ios && pod install && cd ..
# 改めてネイティブからビルド
npx expo run:ios「JS を直して保存したのに直らない」ときは、たいていネイティブの再ビルドを飛ばしています。ライブラリを追加・更新したら、リロードではなくビルドし直す、と覚えておくと無駄な時間を減らせます。
原因3: ライブラリが New Architecture に未対応
prebuild --clean から再ビルドしても消えない場合、そのライブラリ自体がまだ TurboModule に対応していない可能性があります。古いライブラリは旧来の Bridge 前提で書かれていて、Bridgeless モードで動く New Architecture では TurboModule として解決できません。
まず、どのライブラリが原因かを特定します。エラー内のモジュール名(RNSomeModule の部分)を、各ライブラリの README や React Native Directory で照合し、New Architecture 対応の表記があるかを確認してください。
対応版が出ているなら更新します。
npx expo install react-native-some-module@latest対応版がまだ無い場合は、一時的に New Architecture を切るのが現実的です。app.json で次のように設定します。
{
"expo": {
"newArchEnabled": false
}
}設定後は必ず npx expo prebuild --clean からビルドし直してください。app.json を書き換えただけでは反映されません。なお、これは恒久対応ではなく時間稼ぎです。私は移行のとき、対応していないライブラリを1つずつ代替へ置き換え、すべて揃った段階で newArchEnabled を戻しました。
原因4: モジュール名の取り違え・自作スペックの不備
自分でネイティブモジュールを書いている場合は、Codegen に渡すスペック名と、ネイティブ実装で登録している名前がずれていることがあります。getEnforcing('Foo') に対して、実装側が Bar で登録されていれば当然見つかりません。スペックファイル(NativeFoo.ts 等)の名前、@ReactModule / RCT_EXPORT_MODULE の登録名、呼び出し側の3点が一致しているかを確認してください。
切り分けの早見手順
迷ったら、上から順に潰すのが速いです。
- Expo Go で動かしていないか(→ 開発ビルドに切り替え)
- ライブラリ追加後に再ビルドしたか(→
prebuild --cleanから焼き直し) - そのライブラリは New Architecture 対応か(→ Directory で確認、未対応なら一時的に
newArchEnabled: false) - 自作モジュールなら登録名が一致しているか
1と2でほとんどのケースは解決します。3まで来てようやくライブラリ互換性を疑う、という順番が効率的です。
同じ轍を踏まないために
New Architecture への移行は、全アプリを一度に切り替えようとすると原因の切り分けが難しくなります。私は6本のアプリのうち、依存が最も少ない1本を先に移行して上記の手順を固めてから、残りに横展開しました。最初の1本で詰まりどころを洗い出しておくと、2本目以降は驚くほど静かに終わります。
エラーが出たら、まず「これは JS の問題ではなくネイティブバイナリの問題だ」と思い出してください。そこさえ外さなければ、対処は今日まとめた4つの引き出しのどれかに収まります。同じ場面で足を止めている方の参考になれば幸いです。