Blolabel
← Notes
analysis 9 min read

WebDriverAgent 16.15.0 fixes apps bouncing back after launch; phoneshell's open_app already works around the same symptom by never calling it

A session with no application under test gets no quiescence wait, no crash detection and no alert redirect from WebDriverAgent's own machinery. The fix that shipped this week patches one endpoint. Two independently built iPhone agents barely touch it.

WebDriverAgent 16.15.0, released 12 October 2026, adds an asAppUnderTest flag to POST /wda/apps/launch (PR appium/WebDriverAgent#1289, merged 02:54 UTC that day). Without it, an app launched into a session that has no application under test never becomes one: no default quiescence wait, no crash watch, no redirect to a system alert covering it. The PR's own end-to-end test found every post-launch element lookup failing in 3 of 3 runs without the flag and succeeding in 3 of 3 runs with it. We checked the two iPhone-driving agents we have source access to, this project and the newly public iPhone Use, and neither relies on the endpoint the fix touches.

Abstract technical illustration of three luminous signal paths branching from one source across a dark circuit grid, one passing through a glowing checkpoint gate and emerging reinforced, the other two bypassing the gate and fading unchecked into darker blue

What changed, and when

The issue report, appium/appium#22889, describes a session created with appium:autoLaunch: false: no app launches at session startup, so the session starts with no application under test. A later mobile: launchApp call (which maps to POST /wda/apps/launch) opens an app, but FBSession's testedApplication property stays nil, because only two things ever set it: passing a bundleId in the session's startup capabilities, or, as of this PR, passing asAppUnderTest: true to /wda/apps/launch. The PR's description names three concrete consequences of staying nil: the launched app does not wait for quiescence unless the request asks for it, is not watched for system alerts even with respectSystemAlerts on, and is not checked for crashes. The author's own words: "in our UI test suite this made taps land during screen transitions."

16.15.0 is the fifth WebDriverAgent release in nine days. 16.14.0 (4 October) added Surface Duo hinge control; 16.14.1 (7 October) stopped sending the idle check to WebKit services, which we covered on 7 October; 16.14.2 (8 October) fixed Duo scroll actions. None of those three touch testedApplication. 16.15.0 does, and it is the one that matters for anything driving a real iPhone through an app-launch call.

VersionDateChangeTouches testedApplication
16.13.62026-09-30Restore stability wait settings when quiescence throwsNo
16.14.02026-10-04Duo hinge control and secondary display captureNo
16.14.12026-10-07Skip accessibility idle checks for WebKit servicesNo
16.14.22026-10-08Align Duo W3C actions and scrollingNo
16.15.02026-10-12Add asAppUnderTest to /wda/apps/launch (PR #1289)Yes, when the caller opts in

Source: registry.npmjs.org/appium-webdriveragent version timestamps and github.com/appium/WebDriverAgent releases, read 13 October 2026.

The mechanism: three ways to open an app, one of them protected

WebDriverAgent exposes three different endpoints that bring an app to the foreground, and they are not interchangeable underneath. /wda/apps/launchUnattached runs outside any session, through Apple's LSApplicationWorkspace, and cannot touch testedApplication because no FBSession object is involved at all (confirmed by reading FBCustomCommands.m: the route is declared .withoutSession). /wda/apps/activate calls XCUIApplication activate and returns the app; FBSession's own activateApplicationWithBundleId: is four lines and never assigns testedApplication. Only /wda/apps/launch can set it, and before 16.15.0 only by being the app a bundleId at session creation already pointed to.

Three endpoints that open an app, one path to protection /wda/apps/launchUnattached LSApplicationWorkspace .withoutSession route no FBSession object touched testedApplication: never set used by: phoneshell, 1st try /wda/apps/activate XCUIApplication activate 4-line FBSession method returns the app, nothing more testedApplication: never set used by: phoneshell 2nd try, iPhone Use only try /wda/apps/launch XCUIApplication launch the only one that can set it testedApplication: set if asAppUnderTest:true (16.15.0+) used by: phoneshell's last resort, no flag sent testedApplication == nil? decided by which of the three paths opened the app nil: activeApplication getter skips all three no quiescence wait, no crash exception, no alert redirect set: all three apply FBSession.m lines 353-378

What the PR's own test measured

The PR includes an end-to-end run against appium-xcuitest-driver (Appium 3.8.0, UIKitCatalog app, appium:autoLaunch: false, then mobile: launchApp, then two taps that each open a new screen followed immediately by an element lookup on it, three runs each way). Without the flag, WebDriverAgent logged "Quiescence checks are disabled for …" eight times per run, and the lookup right after the second tap failed in every run; the launched app also kept running after the session ended. With asAppUnderTest: true, it logged "set as the application under test" once, then "Waiting up to 10s until … is in idle state" ten times per run, every lookup succeeded, and after the test killed the app with simctl terminate WebDriverAgent correctly reported it "not running, possibly crashed."

Without the flag vs with it, 3 runs each (PR #1289's own validation) without asAppUnderTest with asAppUnderTest:true 0 2 4 6 8 10 8 0 "quiescence disabled" logs / run 3 0 post-tap lookups failed, of 3 runs 0 10 "quiescence wait" logs / run Source: github.com/appium/WebDriverAgent PR #1289 description, "Validation" section, read 13 October 2026.

Where phoneshell and iPhone Use sit

phoneshell's WDAClient.create_session() is normally called with no bundleId at all: "Passing no bundleId means WDA attaches to whatever is on screen instead of launching something, which is what we want for a phone the human also uses" (phoneshell/wda/client.py). Its open_app() then tries three calls in order, launch_unattached, activate_app, launch_app, polling /wda/activeAppInfo for up to four seconds after each before moving to the next (phoneshell/actions.py:860-872). A comment at that exact spot: "launchUnattached first: it is session-free, so it cannot make WDA switch its app-under-test, which is what used to bounce the phone back to the home screen a couple of seconds after every launch." That is phoneshell's own authors describing a symptom consistent with the gap PR #1289 documents, found and worked around by avoiding the endpoint rather than by a flag that did not exist yet. launch_app() is still reached when the first two calls fail to bring the app forward, and it still does not send asAppUnderTest: that parameter is not in WDAClient.launch_app()'s signature at all.

iPhone Use (github.com/zhongerxin/iPhone-use, created 6 October 2026, MIT licensed, 3,582 stars as of this writing) takes a narrower path to the same place. Its session is created the same way, no bundleId in alwaysMatch (server/wda_client.py:194-196), and its pua_launch_app tool opens apps with exactly one call: self.post("/wda/apps/activate", {"bundleId": bundle_id}, timeout=45) (server/wda_controller.py:594-603). No fallback, no /wda/apps/launch at all. Its foreground check, like phoneshell's, polls the session-free /wda/activeAppInfo (server/wda_controller.py:227-232) rather than asking WebDriverAgent's session for its activeApplication.

ToolSession bundleId at creationApp-open call(s) actually usedEver sets testedApplicationForeground check relied onSource
phoneshell (this repo)none, attaches to foregroundlaunchUnattached → activate → launch, in orderonly via the 3rd, last-resort call, and no asAppUnderTest sentown app_state() polling + activeAppInfophoneshell/wda/client.py; actions.py:860-872
iPhone Usenoneactivate onlyneverown active_app() over activeAppInfoserver/wda_client.py:194; wda_controller.py:227,594-603
appium-xcuitest-driver fixture (PR #1289's own test)none (autoLaunch:false)launch, via mobile:launchAppnot before 16.15.0; yes if the caller opts in nowWDA's own activeApplication/testedApplicationgithub.com/appium/WebDriverAgent PR #1289

What it means for anyone driving a phone through WDA

Check it yourself

# WebDriverAgent's own release history and the fix
curl -s https://api.github.com/repos/appium/WebDriverAgent/releases?per_page=10
curl -s https://api.github.com/repos/appium/WebDriverAgent/pulls/1289
git clone --depth 1 --branch v16.15.0 https://github.com/appium/WebDriverAgent.git
grep -n "activateApplicationWithBundleId" -A6 WebDriverAgent/WebDriverAgentLib/Routing/FBSession.m
grep -n "withoutSession" -B2 WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.m | grep -i launchunattached

# phoneshell: the escalating fallback and its one unprotected last resort
grep -n "launch_unattached, self.wda.activate_app, self.wda.launch_app" phoneshell/actions.py
sed -n '376,392p' phoneshell/wda/client.py   # launch_app() has no asAppUnderTest parameter

# iPhone Use: the session it creates and the one call its launch tool makes
curl -s https://raw.githubusercontent.com/zhongerxin/iPhone-use/main/server/wda_client.py | sed -n '188,200p'
curl -s https://raw.githubusercontent.com/zhongerxin/iPhone-use/main/server/wda_controller.py | grep -n "apps/activate\|apps/launch"

What would prove this wrong

We claim phoneshell's three-call fallback keeps its risky path, launch_app() without asAppUnderTest, as a rare last resort rather than the common case. That is wrong if launchUnattached or activate fail to bring an app forward often enough that launch_app() fires on a meaningful fraction of real runs; nothing in this repo currently logs which of the three calls succeeded. By 31 December 2026 we expect open_app() to log which attempt won, and, separately, for WDAClient.launch_app() to still not expose asAppUnderTest, since no open issue in this repo references it today. Both are checkable by grepping runtime/logs and phoneshell/wda/client.py on that date.

Sources