Capacitor Version
💊 Capacitor Doctor 💊
Latest Dependencies:
@capacitor/cli: 8.3.0
@capacitor/core: 8.3.0
@capacitor/android: 8.3.0
@capacitor/ios: 8.3.0
Installed Dependencies:
@capacitor/ios: not installed
@capacitor/android: 8.1.0
@capacitor/cli: 8.1.0
@capacitor/core: 8.1.0
[success] Android looking great! 👌
Other API Details
- Capacitor CLI version: (any version with `signingType` support, i.e. ≥ 5.1.0)
- Platform: Android
- `releaseType`: `'AAB'` (default)
- `signingType`: `'apksigner'`
Platforms Affected
Current Behavior
Describe the bug
When signingType is set to 'apksigner' and releaseType is 'AAB', npx cap build android fails during the signing step with a confusing Java stack trace. The root cause is that apksigner does not support the AAB format — it is an APK-only tool — but the CLI passes the .aab file to it without any validation.
Error output
✖ Signing Release - failed!
[error] Exception in thread "main" com.android.apksig.apk.MinSdkVersionException: Failed to determine APK's minimum supported platform version. Use --min-sdk-version to override
at com.android.apksigner.ApkSignerTool.sign(ApkSignerTool.java:431)
at com.android.apksigner.ApkSignerTool.main(ApkSignerTool.java:94)
Caused by: com.android.apksig.apk.MinSdkVersionException: Failed to determine APK's minimum supported Android platform version
at com.android.apksig.ApkSigner.getMinSdkVersionFromApk(ApkSigner.java:1019)
at com.android.apksig.ApkSigner.sign(ApkSigner.java:301)
...
Caused by: com.android.apksig.apk.ApkFormatException: Missing AndroidManifest.xml
at com.android.apksig.ApkSigner.getAndroidManifestFromApk(ApkSigner.java:975)
...
Steps to reproduce
- Set
signingType: 'apksigner' in capacitor.config.ts (or pass --signing-type apksigner on the CLI), while releaseType is 'AAB' (the default, or explicitly set).
- Run
npx cap build android --keystorepath ... --keystorepass ...
- The Gradle build succeeds, but the signing step fails with the above error.
Additional context
The inverse problem also exists. The default signingType is 'jarsigner':
// cli/src/tasks/build.ts, line 49
signingtype: buildOptions.signingtype || config.android.buildOptions.signingType || 'jarsigner',
jarsigner uses SHA1withRSA / SHA1 digest (hardcoded in signWithJarSigner):
// cli/src/android/build.ts, lines 121–125
const signingArgs = [
'-sigalg', 'SHA1withRSA',
'-digestalg', 'SHA1',
...
SHA-1 signatures are rejected by Android for APKs targeting API 30+. So when building an APK without specifying a signing type, the default jarsigner path produces a binary that modern Android versions will refuse to install. apksigner should be the default for APK builds.
Root cause
The signing dispatch in cli/src/android/build.ts is a simple binary branch with no awareness of releaseType:
// cli/src/android/build.ts, lines 58–62
if (buildOptions.signingtype == 'jarsigner') {
await signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
} else {
await signWithApkSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
}
The else branch routes any value that is not 'jarsigner' — including 'apksigner' — to signWithApkSigner, regardless of whether the output file is an APK or an AAB. When releaseType is 'AAB' (the default), this calls:
apksigner sign --in app-release.aab --out app-release-signed.aab ...
apksigner expects an APK, which is a ZIP with AndroidManifest.xml at the root. An AAB stores its manifest at base/manifest/AndroidManifest.xml, so apksigner cannot find it and throws ApkFormatException: Missing AndroidManifest.xml, which surfaces as the misleading MinSdkVersionException.
The releaseType and signingType options are documented and treated as independent, but they are not — apksigner is fundamentally incompatible with AAB.
Expected Behavior
The CLI should either:
- Throw a clear, actionable error before invoking
apksigner, explaining that apksigner does not support AAB files and instructing the user to use jarsigner for AAB builds or switch releaseType to 'APK'.
- Or automatically select the correct signing tool based on
releaseType, with a warning if the user's explicit signingType is incompatible.
Proposed fix
Replace the binary signing dispatch with logic that is aware of releaseType:
if (releaseTypeIsAAB) {
if (buildOptions.signingtype === 'apksigner') {
throw `apksigner does not support AAB files. Set signingType to 'jarsigner' for AAB builds, or set androidReleaseType to 'APK'.`;
}
await signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
} else {
// APK: prefer apksigner; only use jarsigner if explicitly requested
if (buildOptions.signingtype === 'jarsigner') {
await signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
} else {
await signWithApkSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
}
}
This also requires:
- Removing the hardcoded
|| 'jarsigner' default from cli/src/tasks/build.ts line 49, so buildAndroid can choose the correct tool based on releaseType when the user has not explicitly set signingType.
- Updating the
@default "jarsigner" JSDoc comment in cli/src/declarations.ts line 278 to reflect that the default is now context-dependent ('jarsigner' for AAB, 'apksigner' for APK).
Additional Information
This issue is related to #6909
Capacitor Version
💊 Capacitor Doctor 💊
Latest Dependencies:
@capacitor/cli: 8.3.0
@capacitor/core: 8.3.0
@capacitor/android: 8.3.0
@capacitor/ios: 8.3.0
Installed Dependencies:
@capacitor/ios: not installed
@capacitor/android: 8.1.0
@capacitor/cli: 8.1.0
@capacitor/core: 8.1.0
[success] Android looking great! 👌
Other API Details
Platforms Affected
Current Behavior
Describe the bug
When
signingTypeis set to'apksigner'andreleaseTypeis'AAB',npx cap build androidfails during the signing step with a confusing Java stack trace. The root cause is thatapksignerdoes not support the AAB format — it is an APK-only tool — but the CLI passes the.aabfile to it without any validation.Error output
Steps to reproduce
signingType: 'apksigner'incapacitor.config.ts(or pass--signing-type apksigneron the CLI), whilereleaseTypeis'AAB'(the default, or explicitly set).npx cap build android --keystorepath ... --keystorepass ...Additional context
The inverse problem also exists. The default
signingTypeis'jarsigner':jarsignerusesSHA1withRSA/SHA1digest (hardcoded insignWithJarSigner):SHA-1 signatures are rejected by Android for APKs targeting API 30+. So when building an APK without specifying a signing type, the default
jarsignerpath produces a binary that modern Android versions will refuse to install.apksignershould be the default for APK builds.Root cause
The signing dispatch in
cli/src/android/build.tsis a simple binary branch with no awareness ofreleaseType:The
elsebranch routes any value that is not'jarsigner'— including'apksigner'— tosignWithApkSigner, regardless of whether the output file is an APK or an AAB. WhenreleaseTypeis'AAB'(the default), this calls:apksignerexpects an APK, which is a ZIP withAndroidManifest.xmlat the root. An AAB stores its manifest atbase/manifest/AndroidManifest.xml, soapksignercannot find it and throwsApkFormatException: Missing AndroidManifest.xml, which surfaces as the misleadingMinSdkVersionException.The
releaseTypeandsigningTypeoptions are documented and treated as independent, but they are not —apksigneris fundamentally incompatible with AAB.Expected Behavior
The CLI should either:
apksigner, explaining thatapksignerdoes not support AAB files and instructing the user to usejarsignerfor AAB builds or switchreleaseTypeto'APK'.releaseType, with a warning if the user's explicitsigningTypeis incompatible.Proposed fix
Replace the binary signing dispatch with logic that is aware of
releaseType:This also requires:
|| 'jarsigner'default fromcli/src/tasks/build.tsline 49, sobuildAndroidcan choose the correct tool based onreleaseTypewhen the user has not explicitly setsigningType.@default "jarsigner"JSDoc comment incli/src/declarations.tsline 278 to reflect that the default is now context-dependent ('jarsigner'for AAB,'apksigner'for APK).Additional Information
This issue is related to #6909