Summary
Running multiple independent agent-device CLI processes concurrently from the same host, each targeting a different BrowserStack device via its own freshly-minted connect --json session id (no shared --session/--config between them), causes local daemon state under ~/.agent-device/ to leak across processes — sessions end up bound to the wrong physical device, sometimes silently.
Repro
3 independent processes on the same macOS host, no shared shell state, each running:
agent-device connect browserstack --platform ios --device "<distinct device>" \
--provider-os-version <distinct> --provider-app bs://<app-id> --force --json
# parse data.session
agent-device open <bundle-id> --session "$SESSION_NAME"
# ... normal commands ...
agent-device close --session "$SESSION_NAME"; agent-device disconnect --session "$SESSION_NAME"
Devices used: iPhone 15 Pro/iOS 17, iPhone 16 Pro/iOS 18, iPad Pro 11 2025/iOS 26.
Observed corruption (concrete evidence, one per process)
- Process A (
iPhone 15 Pro): its local remote-connection JSON was silently rewritten mid-run to reference iPad Pro 11 2025's config; a later session name it was given also ended up bound to iPhone 16 Pro — both exactly the other two processes' target devices.
- Process B (
iPhone 16 Pro): repeated Error (INVALID_ARGS): lease_allocate is already bound to session "browserstack:X" on apple device "iPhone 16 Pro" (...), but this request selected --device=iPad Pro 11 2025 and Error (UNAUTHORIZED): Lease does not match session owner (leaseId). One connect call returned a session name literally containing ipadpro11 in response to a request for iPhone 16 Pro. A local daemon crash (Replacing daemon (pid ..., v0.20.5): unreachable) also occurred mid-run, wiping session state.
- Process C (
iPad Pro 11 2025): connect/open silently fell back to a local Apple Simulator instead of the requested BrowserStack device (xcrun exited 4, "app bundle is not installed on this simulator"), and hit UNAUTHORIZED: Lease is not active even though BrowserStack's own dashboard showed the session "ready". daemon stop / daemon stop --clean did not resolve it; explicit --tenant/--run-id isolation flags did not either. The only working mitigation found was forcing a fully unique session name via the (undocumented, env-var-only) AGENT_DEVICE_SESSION variable.
Impact
Two people, or two CI jobs, running agent-device against BrowserStack from the same machine/runner at the same time can silently interfere with each other's sessions and end up issuing commands against the wrong device — a correctness issue, not just flakiness, and potentially a confidentiality concern in shared CI infrastructure.
Note on existing coverage
#1439's concurrency torture lane validates SessionStore/LeaseRegistry invariants via an in-process deterministic scheduler (simulated interleavings within one Node process). That doesn't appear to exercise this failure mode, which is multiple separate OS processes racing on shared on-disk daemon state under ~/.agent-device/, not in-process async interleaving.
Environment
agent-device 0.20.5, macOS host, BrowserStack App Automate
- 3 concurrent independent CLI processes, each targeting a different iOS device, no intentional shared state
Ask
Root-cause + fix for cross-process isolation of BrowserStack session/lease/device-claim state under genuine OS-level concurrency, and/or first-class documentation (not just an undocumented env var) if full isolation isn't immediately feasible.
Summary
Running multiple independent
agent-deviceCLI processes concurrently from the same host, each targeting a different BrowserStack device via its own freshly-mintedconnect --jsonsession id (no shared--session/--configbetween them), causes local daemon state under~/.agent-device/to leak across processes — sessions end up bound to the wrong physical device, sometimes silently.Repro
3 independent processes on the same macOS host, no shared shell state, each running:
Devices used:
iPhone 15 Pro/iOS 17,iPhone 16 Pro/iOS 18,iPad Pro 11 2025/iOS 26.Observed corruption (concrete evidence, one per process)
iPhone 15 Pro): its local remote-connection JSON was silently rewritten mid-run to referenceiPad Pro 11 2025's config; a later session name it was given also ended up bound toiPhone 16 Pro— both exactly the other two processes' target devices.iPhone 16 Pro): repeatedError (INVALID_ARGS): lease_allocate is already bound to session "browserstack:X" on apple device "iPhone 16 Pro" (...), but this request selected --device=iPad Pro 11 2025andError (UNAUTHORIZED): Lease does not match session owner (leaseId). Oneconnectcall returned a session name literally containingipadpro11in response to a request foriPhone 16 Pro. A local daemon crash (Replacing daemon (pid ..., v0.20.5): unreachable) also occurred mid-run, wiping session state.iPad Pro 11 2025):connect/opensilently fell back to a local Apple Simulator instead of the requested BrowserStack device (xcrunexited 4, "app bundle is not installed on this simulator"), and hitUNAUTHORIZED: Lease is not activeeven though BrowserStack's own dashboard showed the session"ready".daemon stop/daemon stop --cleandid not resolve it; explicit--tenant/--run-idisolation flags did not either. The only working mitigation found was forcing a fully unique session name via the (undocumented, env-var-only)AGENT_DEVICE_SESSIONvariable.Impact
Two people, or two CI jobs, running
agent-deviceagainst BrowserStack from the same machine/runner at the same time can silently interfere with each other's sessions and end up issuing commands against the wrong device — a correctness issue, not just flakiness, and potentially a confidentiality concern in shared CI infrastructure.Note on existing coverage
#1439's concurrency torture lane validates
SessionStore/LeaseRegistryinvariants via an in-process deterministic scheduler (simulated interleavings within one Node process). That doesn't appear to exercise this failure mode, which is multiple separate OS processes racing on shared on-disk daemon state under~/.agent-device/, not in-process async interleaving.Environment
agent-device0.20.5, macOS host, BrowserStack App AutomateAsk
Root-cause + fix for cross-process isolation of BrowserStack session/lease/device-claim state under genuine OS-level concurrency, and/or first-class documentation (not just an undocumented env var) if full isolation isn't immediately feasible.