Every Appium session starts with a JSON object called desired capabilities. This is your contract with the Appium server: it tells Appium what platform to target, which device to use, which app to install, and what behaviours you want during the session. Getting capabilities right is the difference between a session that starts in three seconds and one that fails immediately with a cryptic error. This lesson covers every important capability, the naming convention, and the common mistakes.
How capabilities work
When your Java test creates an AndroidDriver or IOSDriver, the first thing it does is send a POST /session request to the Appium server with a capabilities JSON body. Appium reads that object and:
- Identifies the target platform and driver
- Finds a matching connected device
- Installs and launches the app (or connects to a running browser)
- Returns a session ID your test uses for all subsequent commands
A missing or wrong capability fails the session before any test code runs.
Capability namespacing (Appium 2)
Appium 2 enforces W3C capability namespacing. Appium-specific capabilities must be prefixed with appium:. Standard W3C capabilities (platformName, browserName) have no prefix.
When using the Java client's typed options classes (recommended), the library adds the prefix for you:
UiAutomator2Options options = new UiAutomator2Options();
options.setPlatformVersion("14"); // stored as appium:platformVersionIf you build a DesiredCapabilities map manually, add the prefix yourself:
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "Android");
caps.setCapability("appium:automationName", "UiAutomator2");
caps.setCapability("appium:deviceName", "emulator-5554");Essential Android capabilities
| Capability | Type | Description |
|---|---|---|
platformName | W3C | "Android" |
appium:automationName | Appium | "UiAutomator2" |
appium:deviceName | Appium | Name or UDID from adb devices |
appium:platformVersion | Appium | Android API version (e.g., "14") |
appium:app | Appium | Absolute path to APK |
appium:appPackage | Appium | Package name (e.g., com.example.app) |
appium:appActivity | Appium | Launch activity (e.g., .MainActivity) |
appium:noReset | Appium | true to skip app reset between sessions |
appium:fullReset | Appium | true to uninstall and reinstall the app |
appium:newCommandTimeout | Appium | Seconds before idle session is killed |
Use app to install from a local path. Use appPackage + appActivity to launch an already-installed app without reinstalling.
Essential iOS capabilities
| Capability | Type | Description |
|---|---|---|
platformName | W3C | "iOS" |
appium:automationName | Appium | "XCUITest" |
appium:deviceName | Appium | Simulator name or real device name |
appium:udid | Appium | UDID for real device (or "auto" for single connected device) |
appium:platformVersion | Appium | iOS version (e.g., "17.2") |
appium:app | Appium | Absolute path to .app (Simulator) or .ipa (real device) |
appium:bundleId | Appium | Bundle ID to launch an already-installed app |
appium:xcodeOrgId | Appium | Team ID for real device signing |
appium:xcodeSigningId | Appium | "iPhone Developer" |
appium:wdaLocalPort | Appium | Port for WebDriverAgent (default: 8100) |
noReset vs fullReset
These two capabilities control app state between sessions:
| Combination | Behaviour |
|---|---|
noReset: false, fullReset: false | Default. Terminates app and clears session state, keeps app installed |
noReset: true | Skip any reset — app stays installed and retains all data |
fullReset: true | Uninstall and reinstall the app; all user data erased |
For most test suites, the default is correct. Use noReset: true when consecutive tests share state (e.g., a login that persists for the whole suite). Use fullReset: true in environments where a completely fresh install is required.
newCommandTimeout
If your test code crashes or stalls and stops sending commands, the Appium session hangs indefinitely by default. Set newCommandTimeout to a reasonable value:
options.setNewCommandTimeout(Duration.ofSeconds(60));After 60 seconds of no commands, Appium automatically kills the session. This prevents orphaned sessions from piling up in CI.
Typed options vs raw maps
Prefer typed options classes. They provide autocomplete, catch typos at compile time, and handle namespace prefixing:
// Preferred
UiAutomator2Options options = new UiAutomator2Options()
.setDeviceName("emulator-5554")
.setPlatformVersion("14")
.setApp("/path/to/app.apk")
.setNewCommandTimeout(Duration.ofSeconds(60));
AndroidDriver driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), options);Raw maps work but require manually prefixing every Appium capability with "appium:".
Capabilities for cloud providers
BrowserStack, Sauce Labs, and AWS Device Farm accept the same W3C capability format but add their own bstack:options, sauce:options, or AWS-specific capabilities. Check each provider's documentation for the exact wrapper capability name.
For BrowserStack:
HashMap<String, Object> bstackOptions = new HashMap<>();
bstackOptions.put("userName", "your_username");
bstackOptions.put("accessKey", "your_key");
bstackOptions.put("deviceName", "Samsung Galaxy S23");
bstackOptions.put("osVersion", "13.0");
bstackOptions.put("projectName", "My Project");
UiAutomator2Options options = new UiAutomator2Options();
options.setCapability("bstack:options", bstackOptions);
options.setApp("bs://your-app-id");Debugging capability errors
When session creation fails, Appium's error message usually names the problematic capability. The most common causes:
platformVersiondoes not match any connected device's OS versiondeviceNameis misspelled or formatted differently fromadb devicesoutputapppath does not exist or Appium server lacks read permissionautomationNamedriver is not installed (appium driver list --installed)- iOS:
udidis incorrect or the device is not trusted on the Mac