QA How-To
How to Fix Appium WebDriverAgent Failed to Start on iOS
Fix Appium WebDriverAgent Failed to Start on iOS with Xcode, signing, device trust, port, timeout, and CI checks, each with a clear verification step.
18 min read | 3,163 words
TL;DR
Check Xcode and device readiness, then follow the first specific error in the Appium log. Signing failures need a valid WDA profile; port collisions need a unique wdaLocalPort; launch timeouts need runner and device inspection before a longer wait.
Key Takeaways
- Read the first Xcode or port error above the final WDA startup exception.
- Verify the selected Xcode, installed XCUITest driver, and target destination before editing capabilities.
- On real devices, confirm trust, Developer Mode, UI Automation, and runner provisioning.
- Use a unique wdaLocalPort for each parallel real-device worker.
- Increase wdaLaunchTimeout only after WDA builds and starts but answers slowly.
- Prove the repair with a real WebDriver new session under the same CI account and device.
If you need to Fix Appium WebDriverAgent Failed to Start on iOS, the failure usually appears as Appium creates a new XCUITest session, before your first test command reaches the app. Find the earliest Xcode, signing, device, or port error in the server log; that line identifies the repair.
Unable to launch WebDriverAgent because of xcodebuild failure: xcodebuild failed with code 65
TL;DR
On a real iPhone, check Xcode selection, trust the Mac, enable Developer Mode and UI Automation, then sign WebDriverAgentRunner with a development team that can provision that device. On a simulator, start with Xcode and simulator health; a provisioning profile is usually irrelevant. Run Appium with Xcode output visible, because the final WebDriverAgent message often hides the first actionable error.
xcode-select -p
xcodebuild -version
xcrun simctl list devices available
appium driver list --installed
appium driver doctor xcuitest
If those checks pass, create one minimal session and inspect the first failure. Do not raise timeouts until a build succeeds and WDA actually starts. Appium's XCUITest capabilities reference defines the WDA flags used below. For the broader installation path, see the Appium 3 iOS driver setup tutorial.
What the Error Actually Means
Appium receives a WebDriver new-session request, delegates it to the XCUITest driver, and the driver prepares WebDriverAgentRunner. WDA is an XCTest runner that exposes an HTTP endpoint for device automation. On the standard path, Xcode builds and launches the runner; Appium then waits for its status endpoint. Failure at any point in that chain can be reported as a WDA startup failure. The driver overview explains that WDA bridges Appium commands to Apple's XCTest stack.
Separate the stages before changing settings. A missing xcodebuild executable is a host setup problem. An xcodebuild exit code is a build, destination, or signing problem. An installed runner that immediately quits points to device trust, runner launch, or XCTest state. A runner that remains open while Appium cannot reach it points to port forwarding or an HTTP startup problem. The same top-level error therefore does not imply one universal fix.
Capture a debug log from a fresh attempt and search upward from the final exception. The first occurrence of error: from xcodebuild, a provisioning diagnostic, or a port-forwarding message is more useful than the last line. If Appium is already serving another test, stop that session first so the new log has a single device and a single WDA attempt.
appium --log-level debug --log ./appium-wda.log
Run that command in one terminal. In another terminal, issue a session from your normal test client. Then inspect the relevant lines without discarding their order:
grep -nE 'error:|xcodebuild|WebDriverAgent|iproxy|port forwarding|wdaStartFailed' ./appium-wda.log | tail -n 100
Root-Cause Decision Table
| Symptom in the earliest relevant log or device state | Likely root cause | Fix to try first |
|---|---|---|
| xcodebuild not found, wrong developer directory, or no available destination | Xcode command-line selection or incompatible host setup | Select the full Xcode installation and check the driver requirements |
| Device does not appear in Xcode, or runner never opens on a real iPhone | Trust, Developer Mode, UI Automation, or device connection | Prepare the physical device and confirm its identifier |
| xcodebuild exits with code 65 and signing or provisioning errors precede it | WDA signing identity, team, bundle ID, or profile | Sign WebDriverAgentRunner for that exact device |
| Build worked before a toolchain change; old runner or derived products remain | Stale WDA app or build artifacts | Remove only the relevant WDA installation and rebuild |
| Couldn't start port forwarding on port 8100 | Host port already bound or parallel sessions share a port | Assign a distinct wdaLocalPort per real-device session |
| Runner launches but Appium times out waiting for a status response | Slow WDA launch, locked device, or unresponsive XCTest process | Inspect device state, then tune the WDA launch timeout |
| Works locally but fails under a macOS CI agent | Signing key unavailable to the agent or wrong Xcode selection | Prepare an agent-accessible keychain and pin the runner environment |
| A Linux container tries to build WDA | Xcode cannot run in that container | Move iOS execution to a macOS host or attach to managed WDA |
The table is a routing aid, not a substitute for logs. Code 65 can have several causes, and increasing wdaLaunchTimeout cannot repair a provisioning error. For a more complete mobile test strategy after startup works, see the Appium mobile automation guide.
1. Fix Appium WebDriverAgent Failed to Start When Xcode Is Misconfigured
Run these checks on the same Mac account that runs Appium. xcode-select may point to the standalone Command Line Tools directory even though the full Xcode application is installed. WDA requires the Xcode build tools and an SDK that supports the selected target. A test client running on another machine does not change where WDA builds: inspect the Appium server host.
xcode-select -p
xcodebuild -version
xcrun --sdk iphoneos --show-sdk-version
xcrun --sdk iphonesimulator --show-sdk-version
appium driver list --installed
appium driver doctor xcuitest
If xcode-select points to an unintended installation, select the Xcode application your team uses. Replace the path if Xcode is installed under another name. Check the license and first-launch components through Xcode's normal setup flow if xcodebuild asks for them.
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcodebuild -version
Compare installed Appium, XCUITest driver, Xcode, macOS, and iOS against the current driver requirements. Record the versions before changing one component at a time.
Verify: xcodebuild prints the intended Xcode version, appium driver doctor xcuitest reports its prerequisite checks, and the target appears as available in the device list.
xcodebuild -version
xcrun simctl list devices available
appium driver doctor xcuitest
2. Prepare the iPhone and Confirm the Target Device
For a physical device, connect it to the Mac and accept Trust This Computer on the phone. Open Xcode's Devices and Simulators window and wait for the phone to finish pairing or preparing. On iOS versions that expose Developer Mode, turn it on under Settings > Privacy & Security > Developer Mode, complete the restart and confirmation, and enable Settings > Developer > Enable UI Automation. The driver's device preparation guide lists these requirements.
A locked device can prevent an XCTest runner from launching or responding. Unlock it before the first diagnostic session and watch for trust or developer prompts. Ensure the device is selected by its real UDID rather than relying on a display name shared by several phones. Appium provides a driver script to list real devices:
appium driver run xcuitest list-real-devices
Set the connection variables for the remaining real-device examples. Replace the placeholders with values from your environment; the Team ID must belong to the signing team for WDA.
export DEVICE_UDID='<your-device-udid>'
export TEAM_ID='<your-apple-development-team-id>'
Verify: compare the exported UDID with the device list and confirm the phone remains visible while unlocked. A missing match means this branch is still unresolved.
appium driver run xcuitest list-real-devices
printf 'Selected device: %s\n' "$DEVICE_UDID"
Simulator users can skip trust, Developer Mode, and physical-device provisioning. They should instead boot the intended simulator and wait for boot completion:
xcrun simctl boot '<your-simulator-udid>'
xcrun simctl bootstatus '<your-simulator-udid>' -b
If the simulator is already booted, omit the boot command and run bootstatus alone. See Appium desired capabilities for keeping a single explicit target in the session payload.
3. Fix Appium WebDriverAgent Failed to Start When Code Signing Fails
The key evidence is a signing or provisioning diagnostic above xcodebuild exit code 65. Appium's real-device provisioning guide explains why the WDA runner needs a valid profile for the selected phone. The app under test and WebDriverAgentRunner are separate bundles; successful installation of your app does not prove that WDA is signed.
Open the installed WDA project with the supported driver script. In Xcode, select the WebDriverAgentRunner target, enable automatic signing if your team uses it, choose your development team, and inspect the bundle identifier. If your account cannot provision the default identifier, use a unique identifier associated with a valid profile. Keep the Xcode project and Appium's updatedWDABundleId capability aligned.
appium driver run xcuitest open-wda
For a minimal Appium session, xcodeOrgId and xcodeSigningId provide automatic signing inputs. Set xcodeSigningId to the certificate label available on your Mac; Apple Development is common, but confirm it in Keychain Access. The following Python uses only the standard library and sends W3C capabilities to a local Appium server. Start appium in another terminal first. It creates and deletes a Safari session so that a successful new-session response demonstrates WDA startup, not just a compile.
import json
import os
import urllib.request
caps = {
"platformName": "iOS",
"browserName": "Safari",
"appium:automationName": "XCUITest",
"appium:udid": os.environ["DEVICE_UDID"],
"appium:xcodeOrgId": os.environ["TEAM_ID"],
"appium:xcodeSigningId": "Apple Development",
"appium:showXcodeLog": True,
}
payload = json.dumps({"capabilities": {"alwaysMatch": caps}}).encode()
request = urllib.request.Request(
"http://127.0.0.1:4723/session",
data=payload,
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(request, timeout=180) as response:
result = json.load(response)
session_id = result["value"]["sessionId"]
print("WDA-backed session:", session_id)
delete = urllib.request.Request(
f"http://127.0.0.1:4723/session/{session_id}",
method="DELETE",
)
with urllib.request.urlopen(delete, timeout=30):
pass
Save that block as wda_smoke.py and run it after exporting DEVICE_UDID and TEAM_ID. If your certificate label differs, edit only that value. An alternative is appium:xcodeConfigFile pointing to an xcconfig with DEVELOPMENT_TEAM and CODE_SIGN_IDENTITY. Use either the config file or the xcodeOrgId/xcodeSigningId pair for one diagnostic attempt, because competing signing inputs obscure the source of the result.
Verify: the script prints a session ID, Xcode output shows a successful build, and the device briefly displays the automation runner. If it fails, read the first signing diagnostic in appium-wda.log, including the exact bundle identifier and profile name.
python3 wda_smoke.py
grep -nE 'error:|provision|sign|xcodebuild' ./appium-wda.log | tail -n 60
4. Remove a Stale WDA Runner or Derived Build
Use this branch after an Xcode or driver change when a previously working device now has an old WDA runner, or when build logs refer to stale products. Do not clear every Xcode cache as a first response. That is slow, affects unrelated projects, and can hide the original mismatch. First retry with a dedicated derived-data directory and a freshly built WDA project.
Find the WDA project in the installed driver and build it for the selected device. The project path varies with APPIUM_HOME and driver installation, so discover it instead of hard-coding a package version. The build command below depends on the signing setup from section 3.
WDA_ROOT="$HOME/.appium"
if [ -n "$APPIUM_HOME" ]; then WDA_ROOT="$APPIUM_HOME"; fi
WDA_PROJECT="$(find "$WDA_ROOT/node_modules/appium-xcuitest-driver" -name WebDriverAgent.xcodeproj -print -quit)"
test -n "$WDA_PROJECT"
xcodebuild -project "$WDA_PROJECT" -scheme WebDriverAgentRunner -destination "id=$DEVICE_UDID" -derivedDataPath "$PWD/.wda-derived" build-for-testing
A successful build proves compilation and signing, but not device launch. Remove an old WebDriverAgentRunner from the phone only if the log points to a stale installation. On a simulator, use simctl to uninstall its known bundle identifier rather than guessing a custom one.
xcrun simctl uninstall '<your-simulator-udid>' '<your-wda-runner-bundle-id>'
Verify: the isolated xcodebuild command succeeds and the Python smoke script starts a new WDA-backed session. If the build succeeds but the script fails, move to launch, port, or timeout evidence rather than rebuilding again.
test -d "$PWD/.wda-derived/Build/Products"
python3 wda_smoke.py
5. Resolve Port Forwarding and Parallel-Device Collisions
A real-device WDA commonly listens on port 8100 on the device, with the driver forwarding a host port over the device connection. The real error message Couldn't start port forwarding on port 8100 points to that host-side path. If two sessions choose the same host port, one can fail even though both WDA apps are healthy. The appium:wdaLocalPort capability chooses the Mac's forwarded port; appium:wdaRemotePort concerns the device-side listener. Treat them as different values with different jobs.
Inspect the local listener first. lsof identifies the process holding port 8100; do not terminate a process until you know whether it belongs to another active test. In a parallel grid, allocate a stable unique local WDA port per device and keep the assignment with that device's worker.
lsof -nP -iTCP:8100 -sTCP:LISTEN
For example, keep the first worker on 8100 and give a second worker 8101 by adding appium:wdaLocalPort to that worker's capabilities. Modify the caps dictionary in wda_smoke.py before constructing the payload:
caps["appium:wdaLocalPort"] = 8101
Add this line before payload creation. Reserve the port for that worker; multiple Appium servers also need their own server ports. See Appium parallel testing.
Verify: the chosen host port is free before startup, then the smoke session succeeds using that port. If the port is bound by an expected prior worker, choose another port or wait for the worker to finish.
lsof -nP -iTCP:8101 -sTCP:LISTEN
python3 wda_smoke.py
6. Diagnose a Runner That Starts but Never Answers
When WDA appears on the device yet Appium reports a startup timeout, first distinguish slow startup from a runner crash. Watch the phone, keep it unlocked for the diagnostic, and inspect the Xcode log for XCTest termination. Confirm that port forwarding is established before blaming wdaLaunchTimeout. On a simulator, inspect Simulator and Xcode logs if the runner window never appears.
Appium's appium:wdaLaunchTimeout is measured in milliseconds and controls how long the driver waits for WDA to become pingable. Increase it only after observing that the runner eventually starts or that CI hardware is consistently slower. The capability will not make an invalid signature valid. Use a deliberate value for the test environment, then measure actual startup time from log timestamps.
caps["appium:wdaLaunchTimeout"] = 120000
caps["appium:showXcodeLog"] = True
Add these lines before payload creation. The 120000 value is illustrative. Socket hang up or connection refused calls for runner and forwarding checks, not another larger timeout.
Verify: rerun the smoke session and compare the WDA start event with the first successful status response in the log. If it still fails at the increased limit and the runner is absent, revert the timeout change and inspect its exit reason. If status works but the app under test fails later, you have passed the WDA startup problem and should debug the later command separately.
python3 wda_smoke.py
grep -nE 'wdaStartFailed|WebDriverAgent|status' ./appium-wda.log | tail -n 80
7. Repair the macOS CI Signing Environment
A job that succeeds interactively but exits with code 65 under a macOS agent often runs under another user, another selected Xcode, or a keychain that does not expose the private signing key to the agent. Start by printing the toolchain and account identity in the CI job. Keep certificate files and passwords in the CI secret store, not the repository or logs. The XCUITest CI keychain troubleshooting note specifically calls out development-key access restrictions.
whoami
xcode-select -p
xcodebuild -version
security find-identity -v -p codesigning
appium driver doctor xcuitest
If the identity count differs from your interactive account, import an approved development certificate and private key into a dedicated keychain for that CI user. Configure the job to unlock it before the WDA build and pass the keychain path and password through the documented appium:keychainPath and appium:keychainPassword capabilities when needed. Restrict the keychain file and never echo its password. The signing team, provisioning profile, bundle ID, and device UDID must still agree; a keychain alone does not create a usable profile.
Verify: the CI user's security find-identity output includes the expected Apple Development identity, then the same smoke script prints a session ID under the CI account.
security find-identity -v -p codesigning
python3 wda_smoke.py
Docker variant
A Linux Docker image cannot run Apple's xcodebuild or iOS Simulator. Put Appium and the XCUITest driver on a macOS host or supported managed Mac runner. A containerized test client can send WebDriver requests to that Mac, but changing Docker image tags will not give a Linux container an Xcode toolchain. If your provider manages a running WDA, appium:webDriverAgentUrl can attach to its reachable URL; only use that capability when the WDA server already exists and is reachable. This is a different startup path from asking local Appium to build WDA.
export MAC_HOST='your-mac-host'
export WDA_HOST='your-reachable-wda-host'
curl -fsS "http://$MAC_HOST:4723/status"
curl -fsS "http://$WDA_HOST:8100/status"
Replace both host placeholders with actual reachable names. The first command verifies the remote Appium server; the second verifies managed WDA if your setup exposes it. A successful Appium status response alone does not prove an iOS session can start.
How to Verify the Fix
Run one minimal session on the same device and under the same account that failed. The wda_smoke.py example creates a Safari session, prints the returned session ID, and deletes the session. That is a stronger check than merely seeing an Appium server respond at /status. If Safari is not part of your environment, replace browserName with the appium:bundleId of a known installed app, keeping the rest of the capabilities intact.
curl -fsS http://127.0.0.1:4723/status
python3 wda_smoke.py
After the smoke session passes, run one full test through your normal framework. Record the Xcode and driver versions, device UDID, signing method, and WDA port so a later failure can be compared with this passing run.
Prevent It From Coming Back
Pin the Xcode installation and XCUITest driver version in your Mac image or setup manifest, using versions compatible with the devices you actually test. Run the driver doctor and a one-session WDA smoke check whenever the image, certificate, provisioning profile, device OS, or driver changes. Avoid silently updating one component of the stack in the middle of a release. For a repeatable wider workflow, see test automation CI/CD setup.
Keep a device inventory with UDID, iOS version, assigned Mac, development team, and reserved WDA host port. Give each parallel worker a unique host port. Refresh signing assets before they expire and verify that the CI account can access the private key after any runner migration. Document whether a job launches WDA normally, uses a prebuilt runner, or attaches to an already running WDA, because those modes have different failure points.
Interview Questions and Answers
Q: What starts WebDriverAgent in a normal Appium iOS session? The XCUITest driver prepares the WDA runner, uses Xcode tooling to build and launch it on the default path, and waits for its HTTP status endpoint. A working Appium server alone does not prove that WDA started.
Q: Why is xcodebuild code 65 insufficient as a diagnosis? It is an exit status shared by multiple build and signing failures. Read the first Xcode error above it, then check the target destination, team, identity, bundle ID, and profile named there.
Q: Why can an iOS simulator pass while a real iPhone fails? A physical device requires trust, applicable Developer Mode settings, and a valid development signature for WebDriverAgentRunner. The simulator can bypass much of that real-device provisioning path.
Q: What does wdaLocalPort control? It selects the host port forwarded to WDA on a real device. Parallel workers need distinct host ports, while the device-side WDA listener can keep its own port.
Q: When should you increase wdaLaunchTimeout? Only after logs show WDA builds and launches but takes longer than the configured wait to answer. It cannot correct a compile error, invalid signature, or occupied forwarding port.
Q: How do you prove a WDA fix in CI? Run a minimal WebDriver new-session request under the same CI user, device, signing identity, and port assignment as the suite. Require a returned session ID and a clean deletion before starting the broader tests.
The senior Appium interview questions explore additional device and driver trade-offs.
Common Mistakes
- Changing several variables at once: Update one of Xcode, driver, signing, device state, or WDA port per attempt. Otherwise you cannot tell which change mattered.
- Treating every code 65 as a certificate problem: Missing destinations and build errors can also produce a failed xcodebuild command. Preserve the first diagnostic.
- Copying an old WDA bundle ID: A custom updatedWDABundleId must match what your signing setup can provision and what is installed on the device.
- Using useNewWDA for every real-device test: Reinstalling the runner adds startup work and can make physical-device sessions less stable.
- Reusing port 8100 across parallel devices: Reserve one host forwarding port per worker and identify the process before terminating a listener.
- Increasing all timeouts before checking the runner: A larger wait only delays discovery of a compile, trust, or port failure.
- Assuming a Linux container can build iOS tests: The Xcode build and simulator path belongs on macOS; connect remote clients to a Mac-hosted service.
- Verifying only /status on the Appium server: Create a real iOS session and confirm a returned session ID.
Conclusion
Fix Appium WebDriverAgent Failed to Start by following the earliest failing stage: make Xcode and the target available, prepare the real device, sign WebDriverAgentRunner, clear a specifically stale runner, assign a free forwarding port, and only then tune launch timing. Save the passing smoke session as a CI gate so the next WDA failure names its cause before the full test suite starts.
Interview Questions and Answers
Trace an Appium iOS command from client to the device.
The client sends a W3C WebDriver request to Appium. The XCUITest driver handles the session and communicates with WebDriverAgent over HTTP. WDA uses XCTest to drive the iOS UI and returns the result through the same chain.
How would you triage a WebDriverAgent startup failure?
I would capture an Appium debug log with Xcode output, locate the first specific error, and classify the stage as build, signing, installation, launch, or HTTP readiness. I would change one relevant setting and prove it with a minimal new-session request.
Why does xcodebuild exit code 65 not prove a signing failure?
The exit code reports that the build command failed, but several underlying errors can lead to it. I would inspect the preceding xcodebuild diagnostic for a missing destination, compilation problem, certificate, or provisioning error.
How do you configure WDA signing for a physical iPhone?
I choose a development team and signing identity that can provision the WebDriverAgentRunner bundle for the device. In Appium I can supply xcodeOrgId with xcodeSigningId, or point xcodeConfigFile to an approved configuration. I then create a real session to verify launch.
What is the role of wdaLocalPort in parallel testing?
It assigns the Mac host port forwarded to a real device's WDA listener. Each parallel worker needs a different local port so sessions do not collide. I also reserve independent Appium server ports when running multiple servers.
How would you distinguish a WDA timeout from a WDA crash?
I would watch the device and inspect Appium and Xcode logs for a runner process and successful build. If the runner remains alive and eventually answers, a measured timeout increase may help. If it exits or port forwarding fails, the launch or transport cause must be fixed instead.
Why might WDA pass locally and fail in CI?
The CI process can use a different macOS user, selected Xcode, keychain, certificate access, or device mapping. I would print the toolchain and signing identities under the CI account and run the same minimal session there before the full suite.
Frequently Asked Questions
What does Appium WebDriverAgent failed to start mean?
The XCUITest driver could not complete WDA build, installation, launch, or HTTP readiness during session creation. The final error is broad, so inspect the first Xcode or device diagnostic in the Appium server log.
How do I fix xcodebuild code 65 for WebDriverAgent?
Read the Xcode error above code 65. On a real device, confirm the WDA runner has a valid team, signing identity, bundle ID, provisioning profile, and device registration; on a simulator, also check the selected destination and Xcode toolchain.
Why does WDA work on Simulator but fail on a real iPhone?
A physical iPhone adds trust, Developer Mode, UI Automation, and development-signing requirements. Verify the device is visible in Xcode and that WebDriverAgentRunner is provisioned for its UDID.
Should I set useNewWDA to true to fix startup?
Use it only when you need to replace a stale WDA installation or change startup options. Routine real-device sessions generally benefit from reusing a working WDA listener.
What is the difference between wdaLocalPort and wdaRemotePort?
wdaLocalPort chooses the Mac host port used for forwarding to a real device. wdaRemotePort identifies the port used by the WDA listener on that device; parallel workers usually need distinct local ports.
When should I increase wdaLaunchTimeout?
Increase it after logs show a successful build and launch but WDA needs more time to become pingable. A timeout change will not repair signing, Xcode, trust, or port-forwarding failures.
Can I fix WDA startup inside a Linux Docker container?
A Linux container cannot run Apple's Xcode build or iOS Simulator. Run the XCUITest driver on a macOS host or managed Mac service, then connect the test client remotely if needed.
Related Guides
- How to Fix "Cypress failed to start" and cypress verify Errors
- How to Fix Cypress "cy.visit() failed trying to load"
- How to Fix "Playwright Test did not expect test() to be called here"
- How to Fix Appium "An unknown server-side error occurred while processing the command"
- How to Fix Appium "Could not find a connected Android device"
- How to Fix Appium "No Chromedriver found that can automate Chrome" in a WebView