Xcode 27 broke the detached WebDriverAgent launch on older iPhones, and the fix is one launch option
A five-week trail from a beta bug report to appium-xcuitest-driver 12.13.1, the first patch that keyed on the wrong variable, and the one place in our own harness that would stop an overnight run on an upgraded Mac.
Since Xcode 27 shipped on 14 September 2026, a WebDriverAgent runner started with
xcrun devicectl device process launch can come up on an older iPhone, sit on screen for
about 20 seconds, and exit with XCTest error 10300: "Failed to background test runner within 30.0s."
Appium's fix, released in appium-xcuitest-driver 12.13.1 on 20 September, is one launch option,
ActivateSuspended: true, sent through the DVT process-control service instead of devicectl.
The pymobiledevice3 11.8.0 build on our rig already sends that option for every device on iOS 12 or later.
Five weeks from beta report to a one-line fix
The failure was reported against a beta, patched against the wrong variable, reported again two days after the final release, and fixed properly on 20 September. Every entry below names its primary record.
| date | event | host and device | what it established | source |
|---|---|---|---|---|
| 16 Aug 2026 | appium/appium#22636 opened by StevenWong12 | Xcode 27 beta 5, active DDI 27A5237l, iPhone 14 on iOS 26.5.2, Appium 3.6.0, driver 12.3.4 | devicectl-launched runner fails with code 10300 before its test case starts; a probe without Appium or WDA reproduces it | github.com/appium/appium/issues/22636 |
| 16 Aug 2026 | same reporter, RemoteXPC path | same | ProcessControl launched the runner, port 8100 still refused, same 10300. That launch did not yet pass ActivateSuspended | issue #22636 comments |
| 20 Aug 2026 | appium-xcuitest-driver PR #2949 merged (mykola-mokhnach) | n/a | disables the devicectl fallback on iOS and tvOS 27+, keyed on the device's OS version | github.com/appium/appium-xcuitest-driver/pull/2949 |
| 25 Aug 2026 | SunflowersLwtech data point | Xcode 26.6 (17F113), iPad Pro 11-inch on iPadOS 26.6 | same 10300 with no Xcode 27 involved; the same host, WDA build and steps work on an iPhone on iOS 26.6 | issue #22636 comments |
| 14 Sep 2026 | Xcode 27 (27A266a) and iOS 27.0 (24A437) released | n/a | the beta stack becomes the default toolchain on upgraded Macs | developer.apple.com/news/releases |
| 16 Sep 2026 | appium-xcuitest-driver#2978 opened by SMasekoSTS | Xcode 27, one device on iOS 26 and one on iOS 18 | preinstalled WDA stays up about 20 s then closes; session fails at the 60000 ms launch timeout. Neither device runs iOS 27, so the #2949 gate never fires | github.com/appium/appium-xcuitest-driver/issues/2978 |
| 20 Sep 2026 | PR #2980 (navin772), released as 12.13.1 | n/a | adds extraOptions: {ActivateSuspended: true} to the DVT launch and bumps appium-ios-remotexpc to ^5.20.1 | github.com/appium/appium-xcuitest-driver/pull/2980 |
The correction in the middle matters more than the fix at the end. PR #2949 treated this as an
iOS 27 problem and keyed the guard on the phone's OS version. Issue #2978 shows it on iOS 26 and iOS 18
devices driven from an Xcode 27 host. The comment the 20 September patch adds to
lib/device/wda-host-ops.ts says it directly: with the Xcode 27 DDI and XCTest stack, the
synthesized Home press the runner uses to background itself "is ignored on older iOS versions". The
variable is the host's developer disk image, not the phone.
Why a foreground launch now dies at the 30 second mark
An XCTest UI-testing runner is an app that has to get out of the way before it can drive other
apps. Launched in the foreground, it presses Home on itself and waits for the system to confirm it is
in the background. Under the Xcode 27 stack on older iOS, per the patch comment, that press is
ignored, the wait runs out at 30.0 seconds, and the process exits with code 1 before WebDriverAgent
binds port 8100. devicectl device process launch always starts an app in the foreground
and has no flag to change that. Xcode and DVT process control can start the runner already in the
background, which skips the step that fails.
services/dvt/testmanaged/xcuitest.py.The error text itself is old. appium/appium#11702, opened on 19 November 2018, carries the same "Failed to background test runner within 30.0s" from a real-device xcodebuild run. What is new in 2026 is a named trigger that several reporters reproduce on demand.
One caveat keeps this from being tidy. The 25 August data point reproduced 10300 on Xcode 26.6 with an iPad on iPadOS 26.6, while the same host worked with an iPhone. So the Xcode 27 stack is the common trigger in the reports, not the only one. We have one iPhone and no iPad on the rig, so we cannot say what separates the iPad case.
Where our own harness would break
phoneshell launches the runner the way Appium used to: devicectl first, with
--terminate-existing, and a DVT session through pymobiledevice3 developer dvt
xcuitest as the fallback. We read every call site on 21 September. Three of the four recover
on an Xcode 27 host. The one that does not is the one an unattended benchmark depends on.
phoneshell/cli.py, bringup.py, device.py and bench/runner.py at commit cac38a6. The wait lengths are the loop bounds in that code.Our code also carries the same mislabel Appium corrected. The fix hint in launch_wda
reads "this is the iOS 27 failure mode for the devicectl launch path", and the docstrings on both
fallbacks say the devicectl route is "reported broken on iOS 27". Anyone who reads that and checks
the phone's version, sees 26.6.2, and moves on, is looking at the wrong machine.
What to do on a rig that has upgraded, or is about to
Treat the Xcode version as part of the instrument. Our rig runs Xcode 26.6 (17F113) against an iPhone 17 Pro Max on iOS 26.6.2 (build 23G90), checked on 21 September. No published phoneshell number was measured under Xcode 27. We have not reproduced the Xcode 27 failure because we have not upgraded, and this note does not claim we have. The reports are from other people's hardware.
If you drive preinstalled WDA through Appium, 12.13.1 or later is the version that works on an Xcode 27 host with pre-27 phones, and it needs appium-ios-remotexpc ^5.20.1 underneath it plus a working RemoteXPC tunnel. Without the tunnel the driver has no DVT route, and PR #2949 means it will not quietly fall back to devicectl on iOS 27 devices.
If you launch the runner yourself, make the DVT launch with ActivateSuspended the
first try rather than the fallback. The cost is real: a DVT session is tethered, so the runner dies
when the host process does, where a devicectl launch survives the terminal closing. That trade used to
favour devicectl. On an Xcode 27 host it no longer exists, because the detached launch does not come up.
Our judgement is that detached-first is now the wrong default for any harness that runs unattended.
Watch the error you check for. devicectl exits 0 whether or not the runner survives its own
bootstrap, which our cli.py already notes, so the 10300 text shows up in the device log,
not in the launch result. A harness that waits for /status is the only reliable detector,
and it costs the full timeout every time.
Check it yourself
# the fix and its rationale, in Appium's own words
gh pr diff 2980 -R appium/appium-xcuitest-driver | sed -n '1,40p'
gh issue view 2978 -R appium/appium-xcuitest-driver
npm view appium-xcuitest-driver time --json | grep '"12.13'
# what pymobiledevice3 sends on a DVT launch (look for ActivateSuspended)
P=$(python3 -c "import pymobiledevice3,os;print(os.path.dirname(pymobiledevice3.__file__))")
grep -n "ActivateSuspended\|StartSuspendedKey" $P/services/dvt/testmanaged/xcuitest.py
# which toolchain your rig is actually on
xcodebuild -version
xcrun devicectl list devices
# phoneshell's launch sites and the one without a fallback
git clone https://github.com/Blomega/phoneshell && cd phoneshell
grep -n "launch_wda_dvt\|launch_wda(\|recycle_runner" phoneshell/*.py phoneshell/bench/*.py
What would prove this wrong
The claim is that the host's Xcode 27 stack, not the phone's iOS version, decides whether a devicectl-launched runner survives, and that a background launch through DVT avoids it. Two observations would break it. If Xcode 27.1 (in beta since 18 September as 27A9269) restores the devicectl path on iOS 26 phones, the problem was a regression, not a design change, and detached launches are safe again. If an Xcode 27 host with appium-xcuitest-driver 12.13.1 and a working tunnel still logs 10300 against an iOS 26 iPhone, ActivateSuspended is not sufficient. We will upgrade a second Mac to Xcode 27 by 31 October 2026, run phoneshell's 80 tasks against the same iPhone through both launch paths, and publish the launch success counts for each.
Sources
- StevenWong12, usePreinstalledWDA fails on Xcode 27 because the devicectl-launched XCTest Runner cannot enter background, appium/appium#22636, opened 16 August 2026, with comments by KazuCocoa, mykola-mokhnach and SunflowersLwtech (25 and 26 August).
- mykola-mokhnach, fix: disable devicectl launch fallback for preinstalled WDA on iOS/tvOS 27+, appium-xcuitest-driver PR #2949, merged 20 August 2026.
- SMasekoSTS, Preinstalled WebDriverAgent terminates shortly after launch after upgrading to Xcode 27, appium-xcuitest-driver#2978, opened 16 September 2026, closed 20 September 2026.
- navin772, fix(opts): pass ActivateSuspended: true to host opts for backgrounding WDA, PR #2980, released as v12.13.1 on 20 September 2026.
- app/, Failed to install WebDriverAgentRunner on Real Device, appium/appium#11702, 19 November 2018: the same message nearly eight years earlier.
- Apple, Developer releases: Xcode 27 (27A266a) and iOS 27.0 (24A437) on 14 September 2026; iOS 26.6.2 (23G90) on 8 September 2026; Xcode 27.1 beta (27A9269) on 18 September 2026.
- pymobiledevice3 11.8.0,
pymobiledevice3/services/dvt/testmanaged/xcuitest.py,_generate_launch_args:ActivateSuspendedset for product major version 12 and later. Read on our rig 21 September 2026. - Blomega/phoneshell at commit cac38a6:
phoneshell/device.py(launch_wda, launch_wda_dvt, recycle_runner),phoneshell/bringup.py,phoneshell/cli.py,phoneshell/bench/runner.py. github.com/Blomega/phoneshell