Skip to content
Malik Hamza Shabbir
Mobile Engineeringreact nativeandroidgoogle playndk

Fix 'Your app is affected by Google Play's 16 KB page size requirement' in React Native

HSMalik Hamza ShabbirUpdated 8 min read

In short

Google Play's 16 KB page size requirement rejects AABs whose 64-bit native .so libraries are not aligned to a 16 KB boundary, enforced for new submissions and updates after 31 May 2026. Find the offending libs with check_elf_alignment.sh, bump your NDK to r28+ and AGP to 8.5.1+ to fix code you build from source, and upgrade or replace any prebuilt dependency you cannot re-link. Need a hand untangling a stuck native build? See my mobile development work.

Fix 'Your app is affected by Google Play's 16 KB page size requirement' in React Native
On this page

If Google Play just told you "Your app is affected by Google Play's 16 KB page size requirement," it means one or more of the native .so libraries inside your AAB are not aligned to a 16 KB boundary, and Play will start blocking those builds for new submissions and updates after 31 May 2026. The fix is almost always the same three moves: find which .so files are misaligned with check_elf_alignment.sh, upgrade your Android NDK and Android Gradle Plugin so the linker pads segments to 16 KB, and rebuild your offending native dependencies. This post walks the diagnosis end to end so you stop guessing and ship a compliant build.

I have done this on a few React Native and Expo apps now, and the pattern is consistent enough that I can usually tell you which dependency is the culprit before I even unzip the AAB. Let me show you how to confirm it yourself.

What does the Google Play 16 KB page size warning actually mean?

It means Android is moving from 4 KB memory pages to 16 KB pages on newer 64-bit devices, and your shared libraries have to be laid out so they load correctly on both. Apps that ship arm64-v8a or x86_64 native code need every ELF segment aligned to 16 KB, otherwise the loader can fail on 16 KB devices.

The short version: this is purely a 64-bit problem. Your 32-bit armeabi-v7a libraries are irrelevant here because those devices never run 16 KB pages. The warning fires when Play inspects the lib/arm64-v8a/ and lib/x86_64/ folders inside your AAB and finds a .so whose LOAD segments are not aligned to 0x4000 (16384 bytes).

Two things are being checked, and people conflate them:

  1. ELF segment alignment, the p_align value of LOAD segments, which must be at least 16384.

  2. The uncompressed .so being page-aligned and stored uncompressed inside the APK/AAB ZIP, which AGP handles for you with useLegacyPackaging false.


The first one is where prebuilt native dependencies bite you. The second one is mostly a build-system setting. You can be compliant on one and not the other, so check both.

How do I find which .so files are not 16 KB aligned?

Use Google's check_elf_alignment.sh script against your built AAB or APK. It unzips the archive, runs objdump or llvm-readelf over every .so, and prints ALIGNED or UNALIGNED per file so you know exactly which dependency to chase.

First build a release AAB so you are testing the real artifact, not a debug build:

BASH
cd android
./gradlew bundleRelease
## output: android/app/build/outputs/bundle/release/app-release.aab

Then grab the script from the Android platform tools and run it:

BASH
## Fetch the official script
curl -O https://raw.githubusercontent.com/android/ndk-samples/main/16KbPageSizes/check_elf_alignment.sh
chmod +x check_elf_alignment.sh

## Run it against your AAB
./check_elf_alignment.sh app/build/outputs/bundle/release/app-release.aab

You will get output that looks roughly like this:

TEXT
=== ELF alignment ===
lib/arm64-v8a/libreactnative.so: 16384 ALIGNED
lib/arm64-v8a/libhermes.so: 16384 ALIGNED
lib/arm64-v8a/libimagepipeline.so: 4096 UNALIGNED (load segment)
lib/arm64-v8a/librnscreens.so: 16384 ALIGNED
lib/arm64-v8a/libsqlite3x.so: 4096 UNALIGNED (load segment)

Found 2 unaligned libs (only arm64-v8a/x86_64 matters).

The two UNALIGNED lines are your entire problem. Note which dependency each .so belongs to. libimagepipeline.so is Fresco, used by older image stacks; libsqlite3x.so would point at a SQLite-based storage library. That mapping is the real diagnostic work, and the next section covers the usual offenders.

If you do not want to fetch the script, you can spot-check a single file by hand:

BASH
unzip -o app-release.aab -d /tmp/aab
llvm-readelf -l /tmp/aab/base/lib/arm64-v8a/libsqlite3x.so | grep LOAD
## Look at the Align column. 0x4000 is good. 0x1000 means 4 KB, unaligned.

0x4000 is 16 KB and passes. 0x1000 is 4 KB and fails.

Which React Native native libraries are the usual offenders?

In my experience the misaligned .so files almost never come from React Native core anymore. React Native itself, Hermes, and most of the heavily maintained community modules became 16 KB aligned well before enforcement. The libraries that get apps rejected are older or thinly maintained ones that ship prebuilt binaries compiled with an old NDK.

Here is the cheat sheet I work from when triaging an unaligned AAB.








The decision tree is simple. If the unaligned .so is built from source as part of your Gradle build, bumping the NDK fixes it. If it is a prebuilt binary shipped inside the dependency's AAR, you cannot align it yourself; you have to update the dependency to a version the maintainer compiled correctly, or remove it.

For Expo users, this is mostly handled by the SDK version. Expo SDK 52 and later target the right toolchain, and expo prebuild will regenerate native projects with compliant settings. If you are on a managed workflow with EAS Build, upgrading the SDK and rebuilding is usually the whole fix. I covered the broader version-treadmill problem in the Target API 36 migration checklist , because these deadlines tend to land in the same release cycle and it is worth handling them together.

How do I bump the NDK and AGP to align my own native code?

Set the NDK to r28 or newer and the Android Gradle Plugin to 8.5.1 or newer, then rebuild. NDK r28 makes 16 KB the default linker alignment for 64-bit targets, so any .so your project compiles from source comes out aligned without extra flags.

In android/build.gradle (or android/gradle/libs.versions.toml if you use a version catalog), pin the toolchain:

GRADLE
buildscript {
    ext {
        buildToolsVersion = "35.0.0"
        minSdkVersion = 24
        compileSdkVersion = 35
        targetSdkVersion = 35
        ndkVersion = "28.0.12433566"   // r28 or newer
    }
    dependencies {
        classpath("com.android.tools.build:gradle:8.5.1")
    }
}

If you are on an NDK older than r28 and cannot upgrade it yet, you can force the alignment at link time as a stopgap. Add this to your module's build.gradle:

GRADLE
android {
    // ...
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
            }
        }
    }
}

Or pass the linker flag directly for ndk-build / CMake based modules:

TEXT
-Wl,-z,max-page-size=16384

While you are in there, make sure your APK packaging stores .so files uncompressed and page-aligned. With AGP 8.x and the bundle format this is the default, but verify it is not turned off:

GRADLE
android {
    packagingOptions {
        jniLibs {
            useLegacyPackaging = false
        }
    }
}

useLegacyPackaging = true would compress your native libs and break the on-disk alignment that the loader needs, so leave it false.

After changing the toolchain, do a genuinely clean build. Stale .so artifacts in the Gradle cache are the number one reason people "fixed it" and still see UNALIGNED:

BASH
cd android
./gradlew clean
rm -rf ~/.gradle/caches/transforms-*   # nuke cached prebuilt transforms
./gradlew bundleRelease

Then re-run check_elf_alignment.sh on the fresh AAB. Every arm64-v8a and x86_64 line should read ALIGNED.

What if the unaligned .so comes from a dependency I cannot rebuild?

If the misaligned binary ships prebuilt inside a third-party AAR, you cannot align it locally; your real options are upgrade the dependency, patch the package, or drop it. There is no Gradle flag that re-links someone else's compiled .so.

Walk it in this order:

  • Check the dependency's changelog for a release that mentions 16 KB or max-page-size. Most active libraries shipped one through 2025 and early 2026. Bump to it.

  • If the maintainer compiled it but did not publish, look for an open PR or a fork. Sometimes the fix exists and just is not released.

  • If the package is built from source via node_modules, use patch-package to inject the linker flag into its build.gradle, then rebuild. This works because you control the compile step.

  • If none of that lands and the library is abandoned, replace it. An unmaintained native module that blocks Play submission is a liability beyond this one deadline anyway.


A patch-package patch for a source-built module looks like this:

BASH
## After editing node_modules/some-rn-lib/android/build.gradle
npx patch-package some-rn-lib

This is also a good moment to question whether you should be on the New Architecture, since a lot of the freshest native modules only ship aligned binaries for it. If you are weighing that jump, I wrote up the full experience in migrating a legacy app to React Native 0.85 , and the Reanimated 4 migration notes cover a worklets-heavy case where the native side changed underneath us. Untangling stuck native builds like this is a chunk of what I do in my mobile development work .

How do I confirm the build is actually compliant before I upload?

Re-run the alignment script on the final AAB, and for real confidence, test the install on a 16 KB device or emulator image. Play's static check looks at ELF alignment; an actual 16 KB runtime catches loader failures the static check can miss.

Spin up a 16 KB system image in Android Studio's emulator (the API 35 and later images offer a 16 KB page size variant), install your release build, and exercise the screens that use native modules: camera, maps, storage, image rendering. A library that is "aligned" but crashes on load will surface here.

Quick pre-upload checklist:







Native lib in the AABComes fromTypical fix
libreactnative.so, libhermes.soReact Native coreBump to RN 0.77+ (aligned by default)
libimagepipeline.soFresco / older image loadersUpdate RN, or replace with a maintained image lib
libsqlite3x.so, libwcdb.soOlder SQLite storage modulesUpdate to a current release built with NDK r28
libjsi.so from a stale moduleOutdated JSI-based packagesBump the package; rebuild from source
librealm.soRealm versions before the fixUpgrade Realm to a 16 KB compliant release
Map/SDK .so filesThird-party closed SDKsWait for vendor update or pin to their fixed version
CheckCommand / locationPass condition
ELF alignmentcheck_elf_alignment.sh app-release.aabAll arm64/x86_64 libs ALIGNED
NDK versionandroid/build.gradle ndkVersionr28+
AGP versionbuild.gradle classpath8.5.1+
Native packaginguseLegacyPackagingfalse
Runtime sanity16 KB emulator installApp launches, native screens work

When all five pass, upload the AAB. The Play Console warning clears once it scans a compliant build, and you are set ahead of the 31 May 2026 enforcement.

One honest caveat: the static script and Play's scanner can disagree at the margins, usually because of how a specific .so declares its segments. If the script says aligned and Play still complains, the 16 KB emulator test plus a careful read of llvm-readelf -l on each library is how I have always found the straggler.

If you are staring at this rejection on a shipping app and the offending library turns out to be something abandoned in your dependency tree, that is exactly the kind of stuck native build I untangle for clients. Reach out through my contact page and send me the check_elf_alignment.sh output; that one paste tells me most of what I need to scope the fix.

FAQ

What does the Google Play 16 KB page size requirement error mean for React Native?

It means one or more 64-bit native .so libraries in your AAB are not aligned to a 16 KB boundary, which Play blocks for new submissions and updates after 31 May 2026.

How do I find which .so files are not 16 KB aligned?

Run Google's check_elf_alignment.sh script against your release AAB, and it prints ALIGNED or UNALIGNED for every native library so you know exactly which dependency to fix.

Which NDK and AGP versions fix the 16 KB alignment issue?

NDK r28 or newer makes 16 KB the default linker alignment for 64-bit targets, and pairing it with Android Gradle Plugin 8.5.1 or newer aligns any native code you compile from source.

What if the unaligned library comes from a dependency I cannot rebuild?

You cannot re-link a prebuilt third-party .so locally, so you must upgrade the dependency to a 16 KB compliant release, patch it if it builds from source, or replace it.

Do I need to worry about 32-bit armeabi-v7a libraries?

No, the 16 KB page size requirement only affects 64-bit arm64-v8a and x86_64 libraries because 32-bit devices never run 16 KB memory pages.

Working on something like this?

I build web apps, AI features, and mobile products for clients. If this article matches a problem you have, tell me about it.

Start a conversation
HS

Malik Hamza Shabbir · Full-Stack & AI Engineer

I build full-stack and AI products solo: a reputation SaaS in production, RAG pipelines, and React Native apps. I write from what I ship, not from documentation summaries.

Related articles