Desired Capabilities Explained

9 min read

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:

  1. Identifies the target platform and driver
  2. Finds a matching connected device
  3. Installs and launches the app (or connects to a running browser)
  4. 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:platformVersion

If 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

CapabilityTypeDescription
platformNameW3C"Android"
appium:automationNameAppium"UiAutomator2"
appium:deviceNameAppiumName or UDID from adb devices
appium:platformVersionAppiumAndroid API version (e.g., "14")
appium:appAppiumAbsolute path to APK
appium:appPackageAppiumPackage name (e.g., com.example.app)
appium:appActivityAppiumLaunch activity (e.g., .MainActivity)
appium:noResetAppiumtrue to skip app reset between sessions
appium:fullResetAppiumtrue to uninstall and reinstall the app
appium:newCommandTimeoutAppiumSeconds 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

CapabilityTypeDescription
platformNameW3C"iOS"
appium:automationNameAppium"XCUITest"
appium:deviceNameAppiumSimulator name or real device name
appium:udidAppiumUDID for real device (or "auto" for single connected device)
appium:platformVersionAppiumiOS version (e.g., "17.2")
appium:appAppiumAbsolute path to .app (Simulator) or .ipa (real device)
appium:bundleIdAppiumBundle ID to launch an already-installed app
appium:xcodeOrgIdAppiumTeam ID for real device signing
appium:xcodeSigningIdAppium"iPhone Developer"
appium:wdaLocalPortAppiumPort for WebDriverAgent (default: 8100)

noReset vs fullReset

These two capabilities control app state between sessions:

CombinationBehaviour
noReset: false, fullReset: falseDefault. Terminates app and clears session state, keeps app installed
noReset: trueSkip any reset — app stays installed and retains all data
fullReset: trueUninstall 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:

  • platformVersion does not match any connected device's OS version
  • deviceName is misspelled or formatted differently from adb devices output
  • app path does not exist or Appium server lacks read permission
  • automationName driver is not installed (appium driver list --installed)
  • iOS: udid is incorrect or the device is not trusted on the Mac

// tip to track lessons you complete and pick up where you left off across devices.