◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-06-01中級

Metro・EASのビルドが『JavaScript heap out of memory』で落ちるときの対処

RorkやExpoのアプリをビルドしている途中で『FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory』が出て止まる——その原因と、ローカル・EAS両方での具体的な対処をまとめました。

Metro4EAS Build22メモリ4Expo219トラブルシューティング85

画像アセットの多い壁紙アプリをビルドしていたとき、進捗が9割近くまで進んだところで突然ターミナルが赤く染まりました。FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory。何度やり直しても同じ場所で落ちます。私はこの数年、6本の壁紙アプリを並行して運用していますが、アセット数が一定を超えたアプリだけがこの症状に当たりました。

このエラーは「コードが間違っている」のではなく「Node.js に割り当てられたメモリが足りていない」というサインです。原因がコード以外の場所にあるぶん、エラーメッセージだけ眺めても解決の糸口が見えにくいのが厄介なところです。ここでは、ローカルの Metro と EAS Build の両方で、どこに手を入れれば最短で復旧できるかを順を追って整理します。

まず症状と再現条件を切り分ける

このエラーが出る典型的な場面は3つあります。npx expo start でバンドル中に落ちる、eas build のクラウドビルドで Gradle/Xcode 前のバンドル生成段階で落ちる、expo export で本番バンドルを書き出すときに落ちる、です。

共通しているのは「JavaScript のモジュールを大量に同時展開する瞬間」だという点です。Node.js は既定でヒープ上限が決まっており(バージョンや環境によって概ね2GB〜4GB程度)、巨大なアプリや大量のアセットを抱えるとこの上限に先に到達します。つまり、アプリの規模が育った証拠でもあるわけです。

切り分けの第一歩として、本当にメモリ不足なのかを確認します。ビルドコマンドの前に上限を一時的に引き上げてみて、それで通るなら原因はほぼ確定です。

# 一時的にヒープ上限を8GBに引き上げて再現するか確認
NODE_OPTIONS=--max-old-space-size=8192 npx expo start --clear

これで落ちなくなったなら、メモリ不足が原因だと断定できます。逆にこれでも落ちる場合は、循環参照や無限ループなど別の問題を疑う必要があります。

対処1: ローカルのヒープ上限を恒久的に引き上げる

毎回環境変数を打つのは現実的ではないので、package.json のスクリプトに組み込みます。チームや別マシンでも同じ挙動になり、設定漏れを防げます。

{
  "scripts": {
    "start": "NODE_OPTIONS=--max-old-space-size=8192 expo start",
    "export": "NODE_OPTIONS=--max-old-space-size=8192 expo export"
  }
}

Windows では NODE_OPTIONS=... の前置きが効かないので、cross-env を挟むと OS をまたいで動きます。

{
  "scripts": {
    "start": "cross-env NODE_OPTIONS=--max-old-space-size=8192 expo start"
  }
}

数値は搭載メモリの半分程度を目安にします。16GB のマシンなら8192(8GB)が安全圏で、これより大きくしても物理メモリを超えるとスワップが発生して逆に遅くなります。私は手元の Mac では8192で安定し、それ以上に上げても効果はありませんでした。

対処2: EAS Build はリソースクラスを上げる

ローカルではなくクラウドビルドで落ちる場合、NODE_OPTIONS をローカルで設定しても意味がありません。EAS Build のビルドマシン側のメモリを増やす必要があります。eas.json の各プロファイルに resourceClass を指定します。

{
  "build": {
    "production": {
      "android": { "resourceClass": "large" },
      "ios": { "resourceClass": "large" },
      "env": {
        "NODE_OPTIONS": "--max-old-space-size=8192"
      }
    }
  }
}

ここでのポイントは、resourceClass でマシン自体のメモリを増やしつつ、env で Node のヒープ上限も合わせて引き上げることです。マシンのメモリだけ増やしても、Node 側の上限が低いままだと結局同じ場所で落ちます。両方を揃えて初めて効きます。なお large は無料枠の対象外になることがあるため、料金プランは事前に確認しておくと安心です。

対処3: そもそもバンドルに載せる量を減らす

メモリを増やすのは対症療法です。アプリが育ち続ける前提なら、バンドルに載るモジュール量を構造的に減らしておくほうが長く効きます。metro.config.js でインライン展開を有効にすると、起動時に一度に読み込むモジュール数を抑えられます。

// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
 
const config = getDefaultConfig(__dirname);
 
config.transformer.getTransformOptions = async () => ({
  transform: {
    experimentalImportSupport: false,
    inlineRequires: true,
  },
});
 
module.exports = config;

画像アセットについても、巨大な PNG を JS バンドルに巻き込まないよう、require で参照する画像は最適化済みのものに差し替えておきます。私の壁紙アプリでは高解像度画像をリモート配信に切り出した結果、バンドル生成時のメモリ消費が目に見えて下がりました。アセットをローカルに抱え込みすぎないことが、長期的にはいちばん効く予防策だと感じています。

Android側のGradleで落ちる場合は別の上限を疑う

紛らわしいのですが、Androidビルドで似たメモリ不足が出ても、それがNode.jsではなくGradle(JVM)のヒープ不足であることがあります。ログに JavaScript heap ではなく Java heap space や GC overhead limit exceeded と出ていたら、引き上げる場所が違います。この場合は android/gradle.properties のJVM引数を調整します。

# android/gradle.properties
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m

NODE_OPTIONS をいくら上げてもこちらは効きません。エラーメッセージの一語(JavaScript なのか Java なのか)で対処先が分かれる、という点だけ覚えておくと、無駄な試行錯誤を避けられます。私も最初の頃は両者を混同して、Node側ばかり触って時間を溶かしました。ログの主語を最初に読む癖をつけてから、復旧がぐっと速くなりました。

再発させないための習慣

このエラーは、アプリが順調に成長しているときほど不意に現れます。私は2014年から個人でアプリ開発を続け、累計5,000万ダウンロードに育つ過程で、機能やアセットが増えるたびに同じ壁にぶつかってきました。だからこそ、CI のビルドプロファイルには最初から NODE_OPTIONS を入れておき、アセット追加時はバンドルサイズの変化を確認する、という運用を習慣にしています。

次の一手としては、まず手元で NODE_OPTIONS=--max-old-space-size=8192 を付けて再現するかを確かめてください。そこで通るなら、上で紹介した恒久設定に落とし込むだけで、このエラーとはしばらく無縁でいられます。同じ場所で何度も止まって困っている方の、最短の復旧の助けになれば幸いです。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⬡ 開発ツール2026-09-21
exit code 0 で止まるビルド — ログを黙らせていた設定と、空ファイルを通した存在チェック
eas build --local が exit code 0 but produced no further output で止まる件と、0 バイトのスタブが埋め込まれず dyld で落ちる件。どちらも原因は出力を減らす設定でした。自分のスクリプトを棚卸しする手順まで書き残します。
⬡ 開発ツール2026-10-15
EXPO_PUBLIC_ の値が、開発では読めて本番だけ undefined になる書き方を、変換結果で見分けます
API の URL が開発中は読めるのに、ビルドした途端に undefined になる。原因は process.env の書き方でした。babel-preset-expo の変換結果を開発・本番で並べ、置き換わる書き方と残ってしまう書き方を表にしました。
⬡ 開発ツール2026-10-07
日本語の見出しだけ太字にならない — Android で fontWeight が効かないときの切り分けと直し方
Rork で作ったアプリで、iPhone では太字なのに Android では日本語の見出しが細いままになる症状の切り分け記事です。原因の見分け方、fontFamily を重みごとに分ける直し方、確認用コードを載せています。