Fixing the Google Play “16 KB page size” rejection
You upload a build, it’s fine for months, then suddenly Google Play rejects it with something like:
This app does not support 16 KB page sizes.
Support for 16 KB page sizes is required for apps targeting Android 15+.
Your code didn’t change. The requirement did. Here’s what’s actually happening and how to fix it.
Why it happens
Newer Android devices can run with 16 KB memory pages (instead of the classic
4 KB). For an app to run on those devices, its native libraries (.so files)
must be built with ELF LOAD segments aligned to 16 KB — i.e. p_align = 0x4000
(16384). If any shipped .so is only 4 KB-aligned (0x1000), Play rejects
the upload.
This isn’t your app code — it’s the prebuilt native binaries that ship with React Native / Expo (and any native modules).
Check it yourself
Unzip the AAB and inspect the native libs. With the Android NDK’s llvm-readelf
(or any readelf):
# find every .so and print its alignment
unzip -q app.aab -d aab
find aab -name '*.so' -path '*/arm64-v8a/*' | while read f; do
align=$(llvm-readelf -l "$f" | awk '/LOAD/{print $NF}' | sort -u | tail -1)
echo "$align $f"
done
# 0x4000 → good (16 KB aligned)
# 0x1000 → bad (4 KB only) → Play will reject
If you see 0x1000 anywhere under lib/arm64-v8a/, that library is the
problem.
What does NOT work
Don’t try to “fix” it by patching p_align in the ELF headers. A PT_LOAD
whose offset/vaddr aren’t congruent mod 16 KB becomes unloadable — the app crashes
on launch on affected devices. The alignment has to come from how the library was built.
The fix: upgrade the toolchain
16 KB-aligned prebuilts shipped with React Native 0.79+ / Expo SDK 53+. On that
baseline, every arm64 lib reports p_align = 0x4000. So the real fix is
upgrading Expo/RN, not fiddling with the build:
npx expo install expo@^53 react-native@^0.79
npx expo prebuild --clean
The gotcha that bites you next
While you’re bumping targets, expo-modules-core’s Kotlin won’t compile at
compileSdk 36 on some setups. The combination that works: compileSdk 35
with targetSdk 36. AGP allows targetSdk to exceed
compileSdk, so you still meet the Play requirement without breaking the Kotlin build.
Verify before you upload
Re-run the check above on the freshly built AAB. When every arm64-v8a lib shows
0x4000, upload. (Also keep an eye on x86_64 if you ship it.)
The lesson generalizes: store requirements move, and they hit your prebuilt binaries, not your code. Budget for a toolchain bump whenever Play/Apple raises the floor.
I built a tiny, dependency-free CLI to check this automatically — it reports the alignment of every native lib and exits non-zero in CI: store-check.
Get new posts
One email when a new post ships. No spam.