apache/cordova-android · error · CordovaError
Failed to install apk to target: ${output}
Error message
Failed to install apk to target: ${output} What it means
Thrown by Adb.install(target, packagePath) after running `adb -s <target> install <apk>`. adb exits with code 0 even when installation fails and only prints a `Failure [INSTALL_...]` line to stdout, so the library regex-matches /Failure/ on stdout and throws the raw adb output. Two known codes get an appended hint: INSTALL_PARSE_FAILED_NO_CERTIFICATES (unsigned APK, hints at --keystore/--buildConfig) and INSTALL_FAILED_VERSION_DOWNGRADE (hints at uninstall or bump versionCode). Every other adb failure code appears verbatim without a hint.
Source
Thrown at lib/Adb.js:70
const args = ['-s', target, 'install'];
if (replace) args.push('-r');
const opts = { cwd: os.tmpdir(), ...execOptions };
return execa('adb', args.concat(packagePath), opts).then(({ stdout: output }) => {
// adb does not return an error code even if installation fails. Instead it puts a specific
// message to stdout, so we have to use RegExp matching to detect installation failure.
if (output.match(/Failure/)) {
if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (output.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
output += '\n\n' + 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
throw new CordovaError('Failed to install apk to target: ' + output);
}
});
};
Adb.uninstall = function (target, packageId) {
events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
return execa('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() }).then(({ stdout }) => stdout);
};
Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
const args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/);
return execa('adb', args.concat(shellCommand), { cwd: os.tmpdir() })
.then(({ stdout }) => stdout)
.catch(error => Promise.reject(new CordovaError(`Failed to execute shell command "${shellCommand}" on device: ${error}`)));
};
View on GitHub (pinned to 7c1e190064)
Solutions
- Read the Failure [INSTALL_...] code inside the message - it names the exact adb failure; NO_CERTIFICATES and VERSION_DOWNGRADE already carry a hint line.
- For INSTALL_FAILED_VERSION_DOWNGRADE: bump android-versionCode in config.xml (or remove it so it auto-increments), or run `adb -s <target> uninstall <package-id>` first.
- For INSTALL_PARSE_FAILED_NO_CERTIFICATES: sign the build via `-- --keystore=... --alias=...` or `--buildConfig=build.json`, or sign the unsigned APK with apksigner and install manually.
- For INSTALL_FAILED_UPDATE_INCOMPATIBLE: uninstall the existing app (`adb -s <target> uninstall <package-id>`) so the new signature can replace it.
- For INSUFFICIENT_STORAGE / ABI problems: free device space or build for the device ABI (check with `adb shell getprop ro.product.cpu.abi`).
Example fix
// before (config.xml - device already has 10002 installed) <widget ... android-versionCode="10001"> // after <widget ... android-versionCode="10003">
Defensive patterns
Strategy: try-catch
Validate before calling
// Before deploying: catch VERSION_DOWNGRADE / signature mismatch up front
const installed = await Adb.shell(target, `dumpsys package ${packageId} | grep versionCode`).catch(() => null);
// null -> app not installed; otherwise compare the reported versionCode against your APK's before Adb.install Try / catch
try {
await Adb.install(target, apkPath);
} catch (e) {
if (e instanceof CordovaError && /Failed to install apk/.test(e.message)) {
const code = /\[([A-Z_]+)\]/.exec(e.message)?.[1];
if (code === 'INSTALL_FAILED_VERSION_DOWNGRADE' || code === 'INSTALL_FAILED_UPDATE_INCOMPATIBLE') {
await Adb.uninstall(target, packageId);
return Adb.install(target, apkPath); // single retry after uninstall
}
}
throw e;
} Prevention
- Bump android-versionCode (or let Cordova auto-increment it) on every build you deploy.
- Keep one keystore per package id; switching debug/release keystores requires uninstalling first.
- Keep free storage on test devices; check `adb shell df /data` when installs start failing.
- Match the APK ABI to the device (`adb shell getprop ro.product.cpu.abi`) before installing.
When it happens
Trigger: Calling Adb.install (directly, or via `cordova run android` / `cordova emulate android` during the deploy step) when adb stdout contains 'Failure', e.g. 'Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]', 'Failure [INSTALL_FAILED_VERSION_DOWNGRADE]', 'Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE]', 'Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE]'.
Common situations: Deploying a release APK that was never signed; switching branches or resetting CI so the built versionCode is lower than the one already on the device; installing over an app signed with a different keystore; full device storage; APK ABI not matching the device/emulator.
Related errors
- Keystore file does not exist: ${storeFile.getAbsolutePath()}
- Specified build config file does not exist: ${buildConfig}
- Could not find apk architecture: ${arch} build-type: ${build
- Failed to find 'ANDROID_HOME' environment variable. Try sett
- Could not find an available avd port
AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22).
Data as JSON: /api/errors/28ba6d4757830213.
Report an issue: GitHub.