INSTALL_FAILED_INSUFFICIENT_STORAGE — Free /data
INSTALL_FAILED_INSUFFICIENT_STORAGE means /data is full.
20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.
- ✓Basic adb commands
- ✓Android app install flow
- ✓Reading disk usage output
- INSTALL_FAILED_INSUFFICIENT_STORAGE means the /data partition cannot fit your APK — it is a disk problem, not a code bug
- Diagnose first with adb shell df /data; under 200 MB free (or 100% use) explains every symptom
- Top hogs: forgotten debug variants, gigabyte caches, old APKs, and uninstall -k data remnants
- Free /data specifically — clearing photos from /sdcard changes nothing for installs
- android:installLocation is only a hint and needs external storage; App Bundles cut size 20 to 40%
Think of your phone's app storage as a parking garage. Each app is a car, and your new app is a truck looking for a spot. The garage is full — six abandoned demo cars, boxes of old files, no free spaces. The attendant (the package manager) turns your truck away with this error. The fix is not rebuilding the truck smaller (though that helps); it is towing the abandoned cars and clearing the boxes. Check the garage display (df), not the engine.
You run adb install, and instead of Success you get Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE]. The APK is fine — it installed yesterday. Nothing in your code changed. The device just quietly ran out of room, and the package manager is telling you so in its least helpful voice.
This error is pure infrastructure: the /data partition where Android installs apps has less free space than your APK needs. The causes are mundane — six forgotten debug variants, a gigabyte of cached thumbnails, an emulator image sized for 2019 — but the confusion they create is real, because developers instinctively blame code for what is a disk problem.
The misdiagnosis tax is what hurts. Teams rewrite install logic, toggle manifest flags, and regenerate signing keys while df would have shown 0 KB free in ten seconds. Worse, some keep the -k uninstall flag out of habit, preserving the very data directories that keep the partition full across reinstalls.
This article makes the diagnosis mechanical: check /data with df, find the hog with du and package lists, free the right partition, and know what installLocation can and cannot do. Ten minutes of partition hygiene beats a day of code archaeology.
Where Installs Live: the /data Partition Budget
Android installs APKs onto the /data partition: the APK file, its extracted libraries, the data directory, and the Dalvik cache entries all consume /data bytes. When free space on /data drops below what the package manager needs — roughly the APK size plus working headroom — the install aborts with INSTALL_FAILED_INSUFFICIENT_STORAGE before copying a single byte.
The threshold bites earlier than developers expect. A 90 MB APK needs noticeably more than 90 MB free because extraction, dex optimization, and directory creation all draw from the same partition simultaneously. Devices showing 150 MB free can still fail a large install, which is why the 200 MB rule of thumb exists.
Two partitions confuse every diagnosis. Media, downloads, and photos live on /sdcard (often emulated from the same flash but accounted separately), while apps live on /data. Users who proudly free 2 GB of photos change /sdcard and watch the install fail identically — the package manager never looks there.
In code, File.getUsableSpace() reports what the app itself can use, which is handy for pre-flight checks before large downloads. But for install failures the authoritative view is outside the app: adb shell df /data shows the partition truth no app-level API can explain away.
Stale Debug Builds: the Slow Leak on Every Test Phone
Development devices accumulate builds the way desks accumulate cables. Each feature branch gets installed for testing, the branch merges, and the build stays — 90 MB of APK plus data, forever. Six months of branches on a 16 GB test phone consumes gigabytes before anyone notices.
Demo and variant builds multiply the damage. Debug, staging, and production variants of the same app install as separate packages with separate data directories. A tester carrying all three plus last month's demo holds four copies of one app's footprint.
Old APK files add a second layer. Downloaded APKs sitting in Downloads consume /data on most devices (emulated storage draws from the same pool), and CI artifacts pushed to the device for manual installs pile up invisibly.
The fix is inventory plus routine. List packages matching your company prefix, match them against live branches, and uninstall the dead ones. Put device cleanup in the definition of done for merged branches: the branch dies, its builds die with it. Five or fewer test builds per device is a sustainable ceiling. Budget generously on low-end devices: system updates and dex optimization temporarily consume extra /data during installs, so a device idling at 300 MB free can still fail a 100 MB APK mid-write.
Uninstall -k Remnants and Cache Bloat
The -k flag on adb uninstall keeps the app's data directory while removing the APK. It exists for testing backup and restore flows — and it gets used out of habit far beyond that purpose. Every habitual -k uninstall preserves caches, databases, and downloaded files that keep the partition full.
The trap is that reinstalling after -k inherits everything. The new build lands in the old data directory with its gigabytes intact, so the storage pressure that motivated the reinstall survives it. Developers then conclude the new build is bigger or broken, when the directory is simply uncleaned.
Caches deserve their own mention because the system may not clear them promptly. WebViews, image loaders, and HTTP caches grow into hundreds of megabytes on heavy test devices, and trim-caches runs only under system pressure. A deliberate pm trim-caches or in-app eviction policy reclaims this space deterministically.
Default to full uninstalls. Use plain adb uninstall with no flags for routine reinstalls, and reserve -k for the specific test session that needs kept data — documenting why in the test notes. When inheriting a device, pm clear on stale packages is the fastest honest reset. Photograph the drawer before deleting so testers can reinstall what they still need.
Diagnosing With df, du, and Dumpsys
The diagnostic sequence takes two minutes. First, adb shell df -h /data shows partition truth: use percentage and free bytes. Compare free bytes against your APK size — if the APK does not fit with headroom, the case is closed and every further step is cleanup, not debugging.
Second, rank the hogs. Dumpsys package lists and du over /data/data rank residents by size; pm list packages filtered by your prefix finds forgotten variants. The top entries usually explain 80% of the pressure: one giant game, three stale builds, a runaway cache.
Third, reclaim precisely. pm trim-caches with a size argument reclaims cache space system-wide without touching user data. Targeted uninstalls remove dead builds. Clearing a single app's oversized cache from Settings handles the outlier without collateral damage.
In-app, StatFs on the files directory reports what the app can actually use, which powers honest pre-download checks and trimmed-down offline modes. Log the numbers: a support ticket saying install failed with 40 MB free on /data resolves itself, and one saying 4 GB free points at signatures or corrupt APKs instead. Schedule the eviction policy to run on every app start so caches never grow unchecked again.
installLocation and App Bundles: What Actually Shrinks Installs
The android:installLocation manifest attribute declares where the app may live: internalOnly, auto, or preferExternal. It reads like a fix for full /data, but it is a hint the system may ignore — and it helps only when external storage exists and the app supports it. Most modern devices emulate external storage from the same flash anyway.
Widgets, services, and alarm-driven apps must stay internal regardless of the flag; the system will refuse to move them. Apps using certain account and sync features face the same restriction. Setting preferExternal on such apps changes nothing while implying a promise the platform will not keep.
The reliable size lever is the App Bundle. Publishing bundles lets the store generate per-device APKs with only the needed density, ABI, and language splits — typically 20 to 40% smaller downloads. A smaller APK fits where a bloated universal fails, on every partition, with no manifest gambling.
Treat installLocation as documentation of intent and bundles as the actual optimization. Set the attribute honestly, publish bundles always, track download size in CI, and keep /data hygiene as the operational backstop. No manifest flag substitutes for free bytes.
Prevention: Budgets, Gates, and Hygiene That Hold
Prevention is a checklist, not a skill. For developers: cap test-device builds, default to full uninstalls, and check df before theorizing. For CI farms: gate suites on free space, uninstall test APKs post-run, and track APK size with growth alerts. For releases: publish bundles and test installs on low-end devices with nearly-full storage.
For users hitting the error on their own phones, the guidance is concrete: clear cached data from Settings, uninstall unused apps, delete downloaded APKs, and move media off-device. Each step names /data residents — vague advice to free space sends users to delete photos that do not matter.
Large-footprint features deserve budgets. Offline maps, media packs, and ML models should declare their megabytes, check usable space before downloading, and degrade gracefully when room is short. A guarded download that says need 400 MB, have 120 MB beats a crash every time.
The cultural fix is treating device storage as a monitored resource. Dashboards track it on farms, checklists cover it in QA, and support asks for it in tickets. Storage stops being anyone's problem only when it becomes everyone's metric. Pair the attribute with App Bundle publishing so the declaration and the size win ship together.
A Full Device Farm Failed 60% of Installs for 3 Weeks Unnoticed
- Monitor disk on test devices like any other resource — connectivity-green plus disk-full produces failures that look exactly like app regressions.
- Uninstall test APKs after every suite run; installation without cleanup is a slow leak with a pager attached.
- Track APK size in CI with growth alerts, because a 40% fatter APK turns a comfortable device into a failing one silently.
| File | Command / Code | Purpose |
|---|---|---|
| StorageCheck.java | public class StorageCheck { | Where Installs Live |
| CacheJanitor.java | public class CacheJanitor { | Uninstall -k Remnants and Cache Bloat |
| SpaceProbe.java | public class SpaceProbe { | Diagnosing With df, du, and Dumpsys |
| DownloadGuard.java | public class DownloadGuard { | Prevention |
Key takeaways
Common mistakes to avoid
5 patternsRewriting install code when the disk is simply full
Reinstalling over -k remnants and stale debug data
Hoarding debug builds on a small test device
Clearing photos from /sdcard when /data is the full partition
Treating every install failure as a storage problem
Interview Questions on This Topic
What does INSTALL_FAILED_INSUFFICIENT_STORAGE mean?
Frequently Asked Questions
20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.
That's Android. Mark it forged?
5 min read · try the examples if you haven't