You tapped an APK (a beta build a developer sent you, your own build fresh out of CI, an app a colleague shared) and instead of installing, Android put up:
App not installed as package appears to be invalid.
The seven causes, ranked by likelihood
| Cause | Telltale sign | Fix |
|---|---|---|
| 1. Signature mismatch with an installed copy | Some version of the app is already on the phone, maybe in another profile or user; fresh devices install fine. | Uninstall the existing app from every profile, then install. |
| 2. Corrupted or partial download | File size differs from the original; arrived via chat or email. | Re-download over a stable connection. |
| 3. versionCode downgrade | The installed build is newer than the one you are installing. | Uninstall the newer build, or ship a higher versionCode. |
| 4. Device not supported (minSdk or ABI) | Installs on newer phones, fails on this one every time. | Get a build that covers this Android version and CPU. |
| 5. Play Protect blocking the install | Failure comes after a scanning pause, or a Play Protect warning flashed. | Pause scanning for the install, then re-enable it. |
| 6. Insufficient storage | The phone is already warning about space. | Free up roughly twice the APK’s size. |
| 7. targetSdk below 23 on Android 14+ | Fails only on the newest phones; installs fine on older ones. | Raise targetSdkVersion to 23 or higher. |
Older Android versions and some manufacturer skins shorten it to a flat “App not installed.” Either way the dialog is nearly information-free. Several different failures end in some version of it, and the wording varies by Android version and skin: on stock Android 8 and later, “appears to be invalid” itself points at a corrupt or unsigned file, a version downgrade or a test-only build, while a signature clash says “conflicts with an existing package” instead. The phone knows exactly why it refused. It just does not say. So work down the causes in order of likelihood instead of re-downloading five times and hoping.
1. A signature mismatch with the copy already installed
Android’s core install rule: an update only goes on top of an existing app if both are signed with the same certificate. Any difference and the package manager refuses. On stock Android 8 and later the dialog for this one actually reads “App not installed as package conflicts with an existing package”; older versions word it differently. This is the most common cause by far, and it wears two costumes:
- Debug vs release keystore. Android Studio signs debug builds with an auto-generated debug keystore; release builds use the real one. A tester who installed a debug build last week cannot take this week’s release build on top of it, and vice versa. This is the classic “it installed fine yesterday” case.
- A different developer’s build. Two people on the same team building from their own machines, or a Play Store copy versus a direct build — with Play App Signing, the store copy is signed by Google’s key, so your local build never matches it.
Compare the two signing certificates to confirm this cause: run the new APK and the APK the installed copy came from through the APK signature checker, one at a time, and check whether their SHA-256 fingerprints match. It reports each file’s signing certificate fingerprints with no install and no Android SDK on your machine; the APK is uploaded to BetaDrop, read and discarded, not stored. If you no longer have the old APK, the previous build from the same chat thread will do, or the developer can pull the fingerprint from their keystore. Different fingerprints mean Android will refuse this update — unless the new build carries a v3 key-rotation lineage proving it descends from the old key, which is rare outside a deliberate key migration.
The fix is to uninstall the existing app first, then install the new APK. One honest caveat: uninstalling deletes the app’s local data (logins, offline content, in-app settings). For a beta build that is usually acceptable; if it is not, the only alternative is a build signed with the matching key (or one carrying a v3 rotation lineage from it).
Uninstall the old copy from every profile, not just the one you are looking at: Android installs an app once per device and shares that one package between every user and profile that has it, so a copy left in a work profile, a second user, Samsung’s Secure Folder or Xiaomi’s Second Space still blocks a differently signed APK (and blocks an older one as a downgrade, cause 3). That leftover is easy to miss from your main profile, which is how “I already uninstalled it” still ends in this error. Remove it from inside each profile on the phone, or, with USB debugging on (setup is in the adb section below), let adb clear every user at once:
adb shell pm list packages -u example
adb uninstall com.example.appThe first line finds the exact package name: -u also lists apps that are gone from your main profile but still installed for another user. The second removes the package for every user on the device, because adb uninstall without --user defaults to all users. If adb answers “Shell does not have permission to access user” followed by a number, a profile on the phone blocks debugging access, and while it exists adb cannot reach every user, whether or not the app is in that profile (Samsung’s Secure Folder does this just by existing). Name the user instead: adb uninstall --user 0 com.example.app for the main profile, the same with any other number adb shell pm list users shows, and open the locked profile on the phone to uninstall any copy inside it.
2. The file did not survive the trip
A truncated or mangled APK genuinely is an invalid package. Chat apps and email are the usual culprits: interrupted transfers, size caps, or “helpful” re-compression. Compare the byte size against the original if you can, or just re-download over a stable connection and try again. If the file came through three forwarding hops, get a fresh copy from the source instead.
Two files look like APKs but never install anywhere. The first is an .aab renamed to .apk: App Bundles are a publishing format, and they need bundletool to become installable APKs. The second is an unsigned release build straight out of CI (app-release-unsigned.apk is exactly what its name says). No phone-side toggle fixes either; those go back to the developer.
3. You are installing a downgrade
Android never installs a lower versionCode over a higher one. Trying an older APK on top of a newer installed build fails with the same generic dialog. Testers hit this when they open a stale link out of a chat scroll; developers hit it when CI ships every build with versionCode 1. On the phone, uninstall the newer copy first. In the build, bump versionCode on every build you hand out. Wiring it to the CI run number ends this class of failure permanently.
4. The build does not support this device
If the APK’s minSdk is higher than the phone’s Android version, or the APK ships native code only for ABIs the phone does not have (an arm64-only build on an old 32-bit device, say, or an x86 emulator), the install fails on that device and succeeds on others. Drop the file into the APK inspector to read its minSdk, target SDK and ABI list in the browser, then compare against the failing phone’s Android version in Settings → About phone. The fix is a compatible build, not another download.
5. Play Protect stepped in
Google Play Protect scans sideloaded APKs and sometimes blocks an unrecognized beta build outright, occasionally surfacing the failure as the same “app not installed” dialog. To rule it out: open the Play Store → your profile icon → Play Protect → gear icon → turn off “Scan apps with Play Protect”, install, then turn it back on. That last step is not a formality — Play Protect is real malware protection for everything else you install, and it should not stay off because of one beta build. If you do not trust the APK’s source enough to be comfortable pausing the scanner for it, the right move is to not install that APK.
6. The phone is out of space
Installation stages the APK and extracts it, so the phone briefly needs roughly twice the file’s size free. A device running on fumes fails with, again, the same unhelpful dialog rather than a clear storage warning. Clear a few hundred megabytes and retry before concluding anything more exotic.
7. The build targets an API level Android 14 refuses
Since Android 14, the platform will not install any APK whose targetSdkVersion is below 23, whatever the source. The runtime permission model arrived in API 23, and malware was deliberately targeting older levels to opt out of it. The install is blocked and you get, once more, the generic dialog. This one is cause 4 turned inside out: the build fails on the newest phones and installs happily on older ones, so when that is the pattern, do not go hunting for a minSdk problem. The real fix is in the build: raise targetSdkVersion to 23 or higher. To confirm it on the spot, adb install --bypass-low-target-sdk-block app-release.apk installs it anyway; if that succeeds where a plain install failed, this was your cause. Apps installed before the phone moved to Android 14 are left alone; only new installs are blocked.
Make Android tell you the real error
Everything above is deduction because the dialog hides the actual reason. The package manager, though, reports a specific error code — and one adb command surfaces it. Unlock Developer options (Settings → About phone → tap Build number seven times), turn on USB debugging under Settings → System → Developer options, plug the phone in, and run:
adb install -r app-release.apkThe failure comes back named:
adb: failed to install app-release.apk: Failure
[INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.example.app
signatures do not match newer version; ignoring!]The codes map straight onto the causes above:
INSTALL_FAILED_UPDATE_INCOMPATIBLE: signature mismatch (cause 1)INSTALL_PARSE_FAILED_NOT_APK/INSTALL_PARSE_FAILED_NO_CERTIFICATES: corrupt or unsigned file (cause 2)INSTALL_FAILED_VERSION_DOWNGRADE: downgrade (cause 3)INSTALL_FAILED_OLDER_SDK/INSTALL_FAILED_NO_MATCHING_ABIS: unsupported device (cause 4)INSTALL_FAILED_VERIFICATION_FAILURE: blocked by the verifier (cause 5)INSTALL_FAILED_INSUFFICIENT_STORAGE: storage (cause 6)INSTALL_FAILED_DEPRECATED_SDK_VERSION: targetSdk below 23 on Android 14+ (cause 7), spelled out in full as “App package must target at least SDK version 23”INSTALL_FAILED_TEST_ONLY: a build from Android Studio’s Run button (see “If you are the one shipping the build” below)
This feels like a developer move, but any tester with a cable can do it, and it converts twenty minutes of guessing into one line of certainty. If sideloading itself is new territory, the APK install guide covers the first-time setup.
If you are the one shipping the build
Read the list again from the other side and most of it is a distribution problem, not an Android problem. Testers hold stale debug-signed copies because a debug build went out once; chat apps mangle attachments; old APKs get exhumed from message threads and installed over newer ones. Two build habits close most of it: sign every tester build with the same release keystore (the app signing guide covers keystore setup), and bump versionCode on every build.
Never send testers the APK that Android Studio’s Run button built. Run marks it android:testOnly, and Android installs a test-only APK only through adb with the -t flag: the installer a tester taps through refuses it, and on stock Android 8 and later the dialog it shows is, word for word, “App not installed as package appears to be invalid” (INSTALL_FAILED_TEST_ONLY over adb). Build the file you hand out with Build → Generate Bundle(s) / APK(s) → Generate APK(s) (Build Bundle(s) / APK(s) → Build APK(s) in older Android Studio versions) with the debug build variant selected, or from the command line with ./gradlew assembleDebug, or ./gradlew assembleRelease with a signing config so you do not ship the unsigned file from cause 2. Also check that nobody has hard-coded android:testOnly="true" into the manifest. adb install -t does get a test-only build onto a phone plugged into your own machine, which makes it a workaround for you, not a fix for testers. A distribution link does not fix this one, since the flag travels inside the file; betadrop inspect in the BetaDrop CLI fails on a test-only APK before you publish it.
The rest is fixed by distributing a link instead of a file. Upload the APK to BetaDrop and you get an install link and QR code back — free, no account needed to start, nothing for testers to sign up for. The build arrives intact because nothing re-compresses it, and when you upload the next build you send a fresh link, so nobody is digging a three-week-old APK out of a chat thread and hitting a signature mismatch you cannot reproduce.
Frequently Asked Questions
Why does the same APK install on one phone but not another?
Almost always device support: the build's minSdk is higher than the failing phone's Android version, or the APK only ships native code for an ABI the phone does not have, such as an arm64-only build on an older 32-bit device. Inspect the APK to read its minSdk and ABI list, then compare those against the failing device rather than the one where it worked. The inverse case exists too: Android 14 and newer refuse to install any APK whose targetSdkVersion is below 23, so a very old build fails only on the newest phones.
Can I install an APK over the Play Store version of the same app?
Only if it is signed with the identical certificate, which it almost never is. Apps enrolled in Play App Signing are signed by Google's key on the store, while a build handed to you directly is signed with the developer's local keystore. Android treats those as different signers and refuses the update, so uninstall the Play Store copy first and expect its app data to go with it.
Is 'package appears to be invalid' caused by a virus?
No. The message is Android's package manager rejecting an install over a corrupt download, a version downgrade or another of the causes on this page; it is not a malware detection. Play Protect warnings look different and name the app as harmful. The usual caution still applies (only sideload APKs from a source you trust), but this particular error tells you nothing either way about what is inside the file.
Do I need to enable Unknown Sources to fix this error?
No, that setting produces a different screen. Since Android 8, installs are gated per source app, and a blocked source shows a prompt asking you to allow installs from that app, with a shortcut straight to the toggle. If you are seeing the invalid-package message instead, the source permission was already granted and one of the causes on this page is the actual problem.
Why does my APK install from Android Studio but not on testers' phones?
Because Android Studio's Run button builds a test-only APK. Run sets android:testOnly, and Android installs a test-only build only through adb with the -t flag, which is how Android Studio puts it on your own phone; the installer a tester taps through refuses it, on stock Android 8 and later with the invalid-package message. Build the file you share with the debug variant selected, using Generate APK(s) in the Build menu (Build APK(s) in older Android Studio versions), or with ./gradlew assembleDebug instead. adb install -t only helps for a phone plugged into your own machine.
Will restarting the phone fix 'app not installed'?
Occasionally, when a half-finished install session is jamming the installer, so a restart is worth thirty seconds. Beyond that the causes are deterministic: a signature mismatch, a version downgrade, or an unsupported build fails identically after every reboot. If one re-download and one restart have not fixed it, work through the causes in order instead of clearing more caches.
In short
“App not installed” rarely names its real cause, so work through them in order: uninstall the copy that is already installed (in every profile and user on the phone), re-download the file, check you are not downgrading, check minSdk and ABI against the device, check Play Protect, check storage, and on Android 14 or newer, check that the build targets API 23 or higher. If you built the APK yourself, make sure it did not come from Android Studio’s Run button. And when the dialog will not tell you which one you have, adb install -r will.
