Diawi and .aab files: why the upload fails and how to share an APK
· 7 min read
Your CI produces an .aab for Google Play. You drop it on Diawi to send to a tester, and it is refused. Diawi's knowledge base is direct about it: "Android App Bundles (.aab) are not supported" (APK vs AAB). This is not a Diawi quirk. An Android phone cannot install an .aab at all, from any link. This guide explains why, then gives three ways to share the build: build an APK instead, convert the bundle with bundletool, or upload the bundle somewhere that converts it for you.
Why a phone cannot install an .aab
An Android App Bundle is a publishing format. It holds the compiled code and every resource for every device: all screen densities, all CPU architectures, all languages. Google Play takes the bundle and generates split APKs for each device that downloads the app, signed with the app signing key. The phone only ever receives APKs.
So when you share a test build by link, you need an APK. There are two kinds you can make from a bundle: a set of split APKs for one device, which needs a tool to install, or one universal APK that holds everything and installs like any other APK. For link sharing, the universal APK is the one you want.
Option 1: build an APK in the first place
The simplest fix is to ask Gradle for an APK next to the bundle. In most projects:
./gradlew assembleRelease # APK in app/build/outputs/apk/release/
./gradlew bundleRelease # AAB in app/build/outputs/bundle/release/
Upload the APK to Diawi and keep the AAB for Play. Flutter has flutter build apk, and React Native and Expo projects can make an APK from the same Gradle project or build profile. The cost is a second build step in CI. If your CI only makes bundles, or you received a bundle from someone else, use option 2 or 3.
Option 2: convert the .aab with bundletool
Google's bundletool can turn a bundle into one universal APK. You need Java and the bundletool jar, and you must sign the output with a key you hold:
java -jar bundletool.jar build-apks \
--bundle=app-release.aab \
--output=app.apks \
--mode=universal \
--ks=upload.jks \
--ks-key-alias=upload \
--ks-pass=pass:YOUR_STORE_PASSWORD \
--key-pass=pass:YOUR_KEY_PASSWORD
unzip -o app.apks universal.apk
The .apks file is a zip. With --mode=universal it holds one file, universal.apk, which you can upload to Diawi or any other service. If you leave out the signing flags, bundletool signs with a debug key, which works for a quick look but cannot update a build signed with another key.
In CI, keep the keystore and passwords in the CI secret store, never in the repository. More background is in how to install an .aab file on Android.
Option 3: upload the .aab where it is converted for you
AppForShare, the tool we make, accepts .aab uploads on every account plan, Free included (not guest uploads). The worker runs Google's bundletool in universal mode and the build becomes one universal APK that testers download. The .aab itself is not kept. See AAB upload.
Which key signs that APK matters, so here are the rules plainly:
- Free and up: the APK is signed with your workspace's AppForShare test key. It is made on your first .aab and reused for every later one, so test builds update each other.
- Pro and Team: an Admin can add your own upload keystore in Settings. It is checked, sealed with Cloud KMS, and never shown again. APKs from your bundles are then signed with it.
The install page and the build page say which key was used.
Signing keys and the Play version
This is where most "App not installed" errors with converted bundles come from. Android only installs an update over an existing app when both are signed with the same key.
- A test key APK cannot update the app a tester got from Google Play. They uninstall the Play version first. The install page tells them.
- An APK signed with your own upload key updates other builds signed with that key. It installs over the Play version only if that key is also the app signing key. Most apps use Play App Signing, where Google holds the app signing key, so an upload key APK still needs the Play version removed first.
- A universal APK is not what Play delivers. Play makes split APKs per device, so size and some resource behaviour differ. Test store specific behaviour through a Play testing track.
For the full signing picture, see Android app signing for APK and AAB sideloads.
In CI: convert and upload in one job
If you keep Diawi, put the bundletool step right after the bundle task, with the keystore restored from a secret. A GitHub Actions example:
- run: ./gradlew bundleRelease
- name: Universal APK
run: |
echo "$KEYSTORE_B64" | base64 -d > upload.jks
V=1.17.2 # check the bundletool releases page for the newest
curl -sLo bundletool.jar "https://github.com/google/bundletool/releases/download/$V/bundletool-all-$V.jar"
java -jar bundletool.jar build-apks --mode=universal \
--bundle=app/build/outputs/bundle/release/app-release.aab --output=app.apks \
--ks=upload.jks --ks-key-alias="$KEY_ALIAS" \
--ks-pass="pass:$KS_PASS" --key-pass="pass:$KEY_PASS"
unzip -o app.apks universal.apk
env:
KEYSTORE_B64: ${{ secrets.KEYSTORE_B64 }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KS_PASS: ${{ secrets.KS_PASS }}
KEY_PASS: ${{ secrets.KEY_PASS }}
The passwords come from secrets, and GitHub masks secret values in the log. Then upload universal.apk with your existing Diawi step.
If you switch to AppForShare, the conversion step goes away. The CLI and the GitHub Action accept the .aab directly:
npx appforshare upload app/build/outputs/bundle/release/app-release.aab --channel beta
Uploading from CI needs an API token, which is part of Pro and Team. On Free you upload the .aab from the dashboard.
Check the APK before you share it
A converted APK can surprise you. Before sending it, open it in our free APK analyzer, which reads the file in the browser. It shows the package name, version name and code, minimum and target SDK, the signature schemes and signer, and whether the APK is debuggable or signed with a debug key. Two checks catch most problems: the signer is the key you expected, and the version code is higher than the build testers already have.
Installing the APK on the phone
Whichever route you take, the tester's side is the same. They open the link in the phone's browser, download the APK and, the first time, allow that browser to install unknown apps. If Play Protect warns about an unknown app, they tap More details, then Install anyway. AppForShare install pages show these steps in an Install help list, including what "App not installed" usually means. Our guide on enabling unknown sources on Android has screenshots.
Comparing the options
| Build an APK | bundletool yourself | Upload the .aab to AppForShare | |
|---|---|---|---|
| Works with Diawi | Yes | Yes | No, replaces it |
| Extra CI step | A second Gradle task | Java, the jar and a keystore | None, upload the .aab as is |
| Signing key | Your Gradle config | Any key you hold | Test key (Free), or your own keystore (Pro and Team) |
| Size | Depends on your splits | Universal, largest | Universal, largest |
| Cost | Free | Free | Free plan up to 150 MB files |
If you already have an APK step and your build fits Diawi's limits (20 MB on the free account, see Diawi limits), there is no reason to change. If your pipeline only makes bundles, converting on upload removes a step you would otherwise maintain.
Common questions
Will Diawi add .aab support?
We do not know. As of November 2, 2026, its knowledge base says bundles are not supported.
Can I rename an .aab to .apk?
No. The formats are different inside. The phone will refuse the file.
Is the universal APK the same app my users get from Play?
Same code and resources, but packaged differently and usually signed with a different key. Play delivers split APKs signed with the app signing key.
Does AppForShare keep my .aab?
No. Only the universal APK made from it is kept, under your plan's normal storage and link rules.


