stablyai/orca · error · EmulatorError
emulator_error
emulator_error
Error message
adb install failed: ${(result.stderr || result.stdout).trim() || 'unknown error'} What it means
Thrown by installAndroidApk when `adb install` either exits non-zero OR prints a 'Failure'/'Error' marker to stdout/stderr. The dual check is deliberate: adb install is known to exit 0 while reporting 'Failure [...]' on stdout, so a code-only check would let failed installs pass as successes. The message embeds the trimmed adb output for diagnosis.
Source
Thrown at src/main/emulator/android/android-capability-operations.ts:25
import { logcatArgs, parseLogcatLine, type LogcatEntry } from './android-logcat'
import { parseUiAutomatorXml, type AndroidAxNode } from './uiautomator-tree'
// Impure capability operations: compose the pure arg-builders/parsers with the
// command runner. The backend exposes thin delegations to these.
const UIAUTOMATOR_DUMP_PATH = '/sdcard/window_dump.xml'
export async function installAndroidApk(
runner: AndroidCommandRunner,
sdk: AndroidSdkPaths,
serial: string,
apkPath: string,
options?: { reinstall?: boolean }
): Promise<void> {
const result = await runner(sdk.adb, installApkArgs(serial, apkPath, options))
// adb install can exit 0 while printing "Failure [...]" to stdout.
if (result.code !== 0 || /Failure|Error/i.test(`${result.stdout}${result.stderr}`)) {
throw new EmulatorError(
'emulator_error',
`adb install failed: ${(result.stderr || result.stdout).trim() || 'unknown error'}`
)
}
}
export async function launchAndroidApp(
runner: AndroidCommandRunner,
sdk: AndroidSdkPaths,
serial: string,
packageName: string,
activity?: string
): Promise<void> {
ensureAdbOk(await runner(sdk.adb, launchAppArgs(serial, packageName, activity)), 'adb launch')
}
export async function setAndroidPermission(
runner: AndroidCommandRunner,View on GitHub (pinned to 1136503c6a)
Solutions
- Read the embedded Failure code in the message — adb's reason string (e.g. INSTALL_FAILED_UPDATE_INCOMPATIBLE) names the exact problem.
- For signature mismatches, uninstall first: `adb -s <serial> uninstall <package>` then reinstall, or pass { reinstall: true } which maps to adb install -r.
- For ABI mismatches, build a universal APK or include the emulator's ABI (x86_64 for standard emulators).
- Free up emulator storage (`adb shell df /data`) and clear unused packages if INSUFFICIENT_STORAGE appears.
- For downgrades, build a higher versionCode or use adb install -d (downgrade) explicitly.
Example fix
// before: reinstall collides with prior signature
await installAndroidApk(runner, sdk, serial, apkPath)
// after: permit replace of existing install
await installAndroidApk(runner, sdk, serial, apkPath, { reinstall: true }) Defensive patterns
Strategy: validation
Validate before calling
// Pre-check signature/version compatibility before install.
async function canReinstall(runner, sdk, serial, pkg): Promise<boolean> {
const r = await runner(sdk.adb, ['-s', serial, 'shell', 'pm', 'path', pkg])
return r.code === 0 && r.stdout.includes('package:')
} Type guard
import { EmulatorError } from '../emulator-errors'
function isInstallError(e: unknown): e is EmulatorError {
return e instanceof EmulatorError && e.code === 'emulator_error' && /install/i.test(e.message)
} Try / catch
try {
await installAndroidApk(runner, sdk, serial, apk, { reinstall: true })
} catch (e) {
if (e instanceof EmulatorError && /UPDATE_INCOMPATIBLE/.test(e.message)) {
await runner(sdk.adb, ['-s', serial, 'uninstall', pkg])
await installAndroidApk(runner, sdk, serial, apk)
return
}
throw e
} Prevention
- Pass { reinstall: true } when an existing install may be present.
- Uninstall before installing if the signing key changed.
- Build APKs with the emulator's ABI (x86_64) to avoid ABI failures.
When it happens
Trigger: installAndroidApk(runner, sdk, serial, apkPath, options) where the install result has code !== 0, or where /Failure|Error/i matches the concatenation of stdout and stderr. Commonly triggered by an APK signed with an incompatible signature, an already-installed package with reinstall not set, an APK missing a required ABI, or insufficient storage.
Common situations: Reinstalling an APK signed differently without --reinstall (INSTALL_FAILED_UPDATE_INCOMPATIBLE / signatures do not match); INSTALL_FAILED_NO_MATCHING_ABI on a missing architecture; INSUFFICIENT_STORAGE on a full emulator; downgrade attempt (INSTALL_FAILED_VERSION_DOWNGRADE); an APK built for a different minSdk than the emulator's API level.
Related errors
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/f833909d7d3b2fdb.
Report an issue: GitHub.