It's the worst kind of failure. Your Expo Android release build has been running for most of an hour. The native code is compiled, the JavaScript bundle is done, and then:
> Task :app:lintVitalAnalyzeRelease FAILED
...
java.lang.OutOfMemoryError: Metaspace
The task name and the error are from our build; we didn't keep the raw log, so the lines around them are abbreviated.
That's what happened to us on FoosArena, an Expo SDK 57 app with six config plugins (including Google Sign-In and notifications): the build failed after 45 minutes, in :app:lintVitalAnalyzeRelease, after native compilation had already finished. Nothing was wrong with the code. The Gradle daemon had simply run out of one specific kind of memory.
What "Metaspace" means
The JVM keeps the metadata of every loaded class in a native memory area called Metaspace. When a JVM started with a MaxMetaspaceSize limit needs more than that, it throws java.lang.OutOfMemoryError with the detail message Metaspace. Oracle's advice is short: raise the limit.
Why it hits lintVitalAnalyzeRelease
lintVital is the reduced lint run that the Android Gradle Plugin executes on every release build. It checks only "fatal" issues, and if it finds one, the release build is aborted. Our best explanation, which we haven't measured: it runs late in the build, inside the same Gradle daemon that has already loaded the classes of AGP, Kotlin, React Native's Gradle plugins and every Expo module's build logic. Lint then loads its own analysis classes on top. On a large app, that's when Metaspace runs out.
The limit comes from your project. The Expo SDK 57 template's android/gradle.properties sets:
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
That's 2 GB of heap and 512 MB of Metaspace for the whole daemon, more than Gradle's own default (-Xmx512m -XX:MaxMetaspaceSize=384m), but not enough for every app. A small app builds fine with it, while FoosArena didn't.
The manual fix
1. Raise Metaspace and heap
You can't change the limit from inside the failing build. It has to be set before the daemon starts. You have two places to do it.
Option A: user-level, outside the project (recommended). Properties in %USERPROFILE%\.gradle\gradle.properties (or in GRADLE_USER_HOME\gradle.properties) take precedence over the project's file. This also survives npx expo prebuild --clean, which regenerates android/:
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m -Dfile.encoding=UTF-8
Keep in mind that this setting then applies to every Gradle project you build with that Gradle home.
Option B: in the project. Edit org.gradle.jvmargs in android/gradle.properties. In an Expo CNG project that file is generated, so the change disappears the next time you prebuild with --clean. To keep it, use option A.
How much? These are the values that fixed FoosArena: 1 GB of Metaspace and a heap of a quarter of your RAM, between 2 and 4 GB. On a 32 GB machine that's -Xmx4096m. Heap and Metaspace come out of the same physical memory as everything else, so on a 16 GB laptop close the browser and the emulator while you build.
2. Stop the old daemon
A running daemon keeps its old settings, so stop it before trying again:
cd android
.\gradlew.bat --stop
.\gradlew.bat app:bundleRelease
3. What not to do: turn lint off
You'll find advice to disable the check with lint { checkReleaseBuilds = false } (or the older lintOptions { checkReleaseBuilds false }). It makes the error go away, because the task no longer runs, but it also removes the check that stops a release with "fatal" issues. Treat it as a last resort for a one-off build, not as the fix.
How WinBuild handles it
After FoosArena's failed attempt, we made WinBuild set the memory itself, the same way as option A, without touching your project:
- Each build attempt gets its own Gradle home, and its
gradle.propertiessetsorg.gradle.jvmargsto a heap of a quarter of your RAM, between 2,048 and 4,096 MB, with-XX:MaxMetaspaceSize=1024mand-Dfile.encoding=UTF-8. Since that file takes precedence over the project's, yourandroid/gradle.propertiesis never edited. - Lint vital stays on. You keep the safety check and lose the crash.
- The values used are in the build report (
gradleJvmArgs). If memory still runs out, the failure is reported asOUT_OF_MEMORYinstead of a generic Gradle error. winbuild doctor --perfwarns you before the build if the PC has less than 16 GB of RAM.
With this change, FoosArena's next build succeeded. Its AAB passed verify with playUploadable: true, with the same upload certificate as build 17, which Google Play had accepted.
Sources
External sources consulted on 20 October 2026. Figures about WinBuild come from our own build records.
- Oracle: Troubleshoot memory leaks (Metaspace)
- Gradle: Build environment (gradle.properties, org.gradle.jvmargs)
- Expo SDK 57 template, android/gradle.properties
- Android Gradle Plugin 8.12.3 DSL source, Lint.kt (mirror)
Private alpha
Building Expo Android releases on Windows?
WinBuild is in private alpha. Testers get the package directly and help validate different machines and Expo projects.