In Appium, what goes wrong when two simultaneous iOS sessions share one appium:wdaLocalPort?
answer
- one host port, one owner
- two shapes: loud and silent
- wrong iPhone answers the commands
- the derived-data directory clashes too
basics
~20 sEither the second iOS session fails to start because the port is already taken, or it reaches the WebDriverAgent already listening there and drives the first iPhone. The silent second case looks exactly like flaky tests.
solid answer
~40 s`appium:wdaLocalPort` is the host-side port the XCUITest driver reaches WebDriverAgent on, so sharing it between two simultaneous iOS sessions means two sessions aimed at one channel. The loud outcome is a startup failure: the port is bound, and the second session never gets an agent. The quiet outcome is worse — the second session connects to the agent already listening there, and its commands are executed on the first iPhone. The stage-crew call-sheet suite then reports assertion failures that read like flake, while one device does twice the work and the other never moves. Distinct `appium:udid` values do not prevent this, because the udid picks the device and the port picks the channel. Each concurrent Apple session needs its own `appium:wdaLocalPort` and its own `appium:derivedDataPath`.
go deeper
Know that appium:wdaLocalPort is the host port an iOS session uses to reach WebDriverAgent, and that two sessions on one Mac each need their own value.
Explain both failure shapes: a startup error when the port is bound, and a silent redirect when the second session reaches the agent already listening there.
Walk through the triage: an idle device beside a busy one, resolved ports logged per session id, leftovers from killed runs, and the derived-data directory checked alongside the port.
Own the prevention: reserved bands per lane, values derived from the worker index, and a startup assertion that a session is driving the device it asked for so silent misrouting becomes a loud failure.
## What the capability addresses On Apple platforms the XCUITest driver does not automate the interface from the host. It builds, installs and launches WebDriverAgent on the device, and then drives the app under test by sending HTTP requests to that agent. `appium:wdaLocalPort` is the host-side port used for those requests: the driver forwards it to the agent, and every command in the session travels through it. It is the exact counterpart of `appium:systemPort` on Android, sharing none of its letters. Because it is a host port, it obeys host rules. One process owns a given number at a time, and any second claim on it is resolved by the operating system rather than by Appium. ## Failure shape one: the loud one The well-behaved outcome is that the second session never starts. The port is already bound, the forward cannot be established, and session creation fails. This is annoying and obvious: the run reports a startup error, one worker dies, and the cause sits close to the symptom. Teams that meet only this shape tend to conclude that a port clash always announces itself. ## Failure shape two: the silent one The expensive outcome is that the second session starts successfully and talks to the agent already listening on that port — the one belonging to the first iPhone. Now two workers are driving one device through one channel. The stage-crew call-sheet suite behaves like this: - Steps that only read state often pass, because the first device happens to be on a similar screen. - Steps that depend on the worker's own earlier actions fail, because another worker's actions landed in between. - The second device never moves, but nothing watches devices, so nobody notices. - Failures wander between runs, which is the signature people file as flake. The cost is not the failed run; it is the week spent hardening waits and locators that were never the problem. ## The derived-data clash that travels with it A fleet that has not allocated `appium:wdaLocalPort` usually has not allocated `appium:derivedDataPath` either, and the two failures arrive together. Derived data is where `xcodebuild` writes while it prepares WebDriverAgent. Two concurrent sessions pointed at one directory contend over files instead of sockets, and the result is another second session that either fails to start or inherits the first one's output. When triaging, treat the two as one checklist entry: the Apple lane allocates a port *and* a directory per session. ## Triaging a Mac that runs several sessions 1. Compare the number of live Appium sessions with the number of devices actually moving. One idle device beside a busy one is the tell. 2. Read the resolved `appium:wdaLocalPort` for each session out of the run log, next to the session id. If it is not logged, fix that first — it is the cheapest permanent instrument. 3. Check what is already bound on the host before a run starts, including leftovers from a run that was killed rather than closed. 4. Confirm each session was also given its own `appium:derivedDataPath`. 5. Only after all four does the locator or the wait deserve suspicion. ## Why the udid does not save you A natural defence is that the two sessions name different devices, so surely they cannot collide. They can. `appium:udid` selects which device the session is meant to drive; `appium:wdaLocalPort` selects the channel the driver actually uses to speak to an agent. Once the port has an owner, the request goes to that owner, and the udid the session asked for is never consulted again. The two capabilities answer different questions, and only one of them decides where a command lands. ## Preventing it rather than diagnosing it The fix is allocation, not retry policy. Derive `appium:wdaLocalPort` from the worker index inside a band reserved for the Apple lane, so two workers cannot compute the same number and no other lane wanders into the range. Pin `appium:derivedDataPath` the same way. Where an agent is deliberately already running and shared, that is a different arrangement expressed through `appium:webDriverAgentUrl` — a decision to make explicitly rather than something to fall into by leaving a port at its default. One more habit pays for itself: assert at the start of a session that the device it is driving is the device it asked for. A cheap check against the expected `appium:udid` turns the silent failure shape into a loud one, which is the whole difference between an afternoon and a fortnight.
- Why do distinct appium:udid values not protect two iOS sessions from this?Because they answer different questions. The udid selects which device a session is meant to drive; `appium:wdaLocalPort` selects the channel the driver actually uses. If two sessions name different devices but the same port, the second session's requests still arrive at the agent already listening there, and the udid it asked for is never consulted again.
- What single instrument would have caught this in the first failing run?Logging the resolved `appium:wdaLocalPort` and the target `appium:udid` together with the session id at session start. Two lines with the same port and different udids identify the fault immediately, and the log survives the run, so a failure reported by someone else is still diagnosable a day later.
A shared appium:wdaLocalPort is like two stage managers dialling the same backstage extension. The call connects every time, just never to the person either of them meant.
saying these in an interview costs you the question
- Expects a shared agent port to always fail loudly at startup
- Blames flaky locators when two sessions share one agent channel
- Thinks WebDriverAgent picks a free host port by itself
- Ignores appium:derivedDataPath when two Apple sessions build at once
- Assumes distinct udid values keep two iOS sessions apart