Skip to content
karagöz docs

karagoz install

Installs an app from one APK file, or on an iOS simulator or iPhone from a .app or .ipa, or replaces the installed version of the same app.

  • Android emulator Done
  • Android device Done
  • iOS simulator Done
  • iOS device Done
~/app
karagoz install <apk> [--device <id>]
  • On Android: platform-tools 30.0.0 or newer.
  • On an iOS simulator: Xcode, opened once.
  • On an iPhone: Xcode 15 or newer, paired with the Mac, Developer Mode on (Settings > Privacy & Security > Developer Mode).
Option Default Meaning
<apk> Path to an .apk file, or for a simulator or an iPhone a .app directory or an .ipa file. A relative path is resolved against the current directory. One .apk only: split APKs, .apks and .aab are not supported.
--device <id> see Device selection
~/app
karagoz install /Users/me/app/build/outputs/apk/debug/app-debug.apk
{"device":"emulator-5554","path":"/Users/me/app/build/outputs/apk/debug/app-debug.apk"}exit 0
{
"device": "emulator-5554",
"path": "/Users/me/app/build/outputs/apk/debug/app-debug.apk"
}

The package name is not reported.

device string
Serial, or the simulator's or iPhone's UDID.
path string
The absolute path of the app, as given (symlinks are not followed).
CodeWhen
INVALID_ARGS'<path>' is not an .apk, .app or .ipa file, no file at '<path>', '<path>' is not a file, or for a .app '<path>' is not a directory. Checked before any device call. Once the device is picked, and before anything is sent to it: '<path>' is an Android app; <udid> is an iOS simulator (is an iPhone on one) or '<path>' is an iOS app; <serial> is an Android device.
INSTALL_FAILEDAndroid refused the APK. The message is adb's, and reason is Android's code, as in the example.
ADB_FAILEDA failure without an Android code, such as Error: device is still booting.
ADB_TIMEOUTAfter 30 s plus 1 s for every started MB of the APK. On a phone, the install may be held on a prompt (Play Protect, or the maker's check for USB installs) that waits for a tap; if it is accepted later, the app still installs.

INSTALL_FAILED codes seen on the emulator:

reason Cause
INSTALL_FAILED_VERSION_DOWNGRADE A lower versionCode than the installed app.
INSTALL_FAILED_UPDATE_INCOMPATIBLE Signed with another key.
INSTALL_PARSE_FAILED_NOT_APK
INSTALL_FAILED_DEPRECATED_SDK_VERSION targetSdkVersion below 24 on Android 16.

On a phone, Android’s code can come from the phone’s own install check, with text that says someone cancelled when nobody did:

reason Message Cause
INSTALL_FAILED_VERIFICATION_FAILURE Play Protect refused the app.
INSTALL_FAILED_USER_RESTRICTED Install canceled by user Xiaomi, Redmi, POCO: “Install via USB” is off, or its prompt ran out.
INSTALL_FAILED_ABORTED User rejected permissions vivo, iQOO: the same.

Every install can also get the device selection errors and the adb errors.

  • Runs adb install -r --no-incremental. Platform-tools older than 30.0.0 forward --no-incremental to the device, and the device rejects it.
  • There is no way to pass -g (grant runtime permissions), -d (allow a downgrade) or -t. An APK marked testOnly, which Android Studio’s Run button can produce, fails with INSTALL_FAILED_TEST_ONLY.
  • Android installs the app for every user on the device that allows adb installs; Secure Folder, Dual Messenger and work profiles get it too.
  • On Android 17, installing over the app in front puts a system placeholder in its task. The app restarts 2 to 20 s after install returns. A launch in that window exits 0, reports com.android.systemui/com.android.wm.shell.packageupdate.PackageUpdateActivity as activity and starts nothing.

Takes a .app bundle directory or an .ipa, built for the simulator, and runs simctl install.

{
"device": "2EC769C1-2EF2-47EB-A441-E6843968F039",
"path": "/Users/me/Build/Products/Debug-iphonesimulator/Probe.app"
}
  • A build for a physical iPhone is refused: INSTALL_FAILED with reason IXUserPresentableErrorDomain:4 and simctl’s message, which says the executable has no code for the simulator. Other refusals carry simctl’s own <domain>:<code>, such as IXErrorDomain:13 for a bundle without Info.plist. A failure without one stays SIMCTL_FAILED.
  • A lower CFBundleVersion than the installed app is not refused. The app’s data is kept across reinstalls.
  • SIMCTL_TIMEOUT after 30 s plus 1 s for every started MB of the file; a .app gets 31 s.
  • Over an app stopped in a debugger (an Xcode breakpoint or pause), the install waits for the debugger. The call ends in SIMCTL_TIMEOUT, and the app is installed once it continues or the debug session ends. Meanwhile other calls to that simulator can time out too.

Takes a .app bundle directory or an .ipa built and signed for this iPhone, and runs devicectl device install app. Karagöz does not re-sign the app: build it for the phone in Xcode or Flutter, which sign it with a profile that includes this iPhone.

  • The phone checks the signature first. A refusal is INSTALL_FAILED, whose reason is the phone’s own <domain>:<code>, the deepest error devicectl reports. The message names the cause, then says that Karagöz does not re-sign:
    • a simulator build: MIInstallerErrorDomain:13 with 0xe8008014 (The executable contains an invalid signature.)
    • an unsigned app: 0xe800801c (No code signature found.)
    • a profile that does not include this iPhone: 0xe8008015 or 0xe8008012, from public reports (not measured here)
  • A bundle without Info.plist is refused on the Mac: com.apple.dt.CoreDeviceError:3000.
  • A reinstall replaces the version, a lower one too, and keeps the app’s data. Installing over the running app stops it. A locked iPhone installs too.
  • DEVICECTL_TIMEOUT after 30 s plus 1 s for every started MB of the file; a .app gets 31 s.
MCP equivalentinstall {"apk":"/Users/me/app/build/outputs/apk/debug/app-debug.apk"}MCP tools

Arguments: apk (required), device. The names are the CLI's option and positional names.