Device setup hides iOS simulators that were never booted, and says no simulator is installed#16083

openedOct 5, 2026byMmaciekzygmunt

What happened

I installed Xcode and the iOS 27.0 runtime (Xcode Settings → Components). In the T3 Code desktop app, "Set up devices" → Simulators still says:

Xcode is installed, but no iOS Simulator is available. Install a runtime in Xcode Settings → Components.

"Check again" does not change it. The runtime and 11 simulators exist on the machine.

Diagnosis

T3 Code gets the device list from expo-device-hub 0.12.0 (GET /api/devices). On this machine the hub returns "simulators": [] with no iOS error. xcrun simctl list devices --json from the same user returns 11 devices, all "isAvailable": true.

The hub's listIosSimulators (in expo-device-hub/dist/server/index.mjs) ends with:

js
1.filter((device) => device.lastUsedAt !== undefined)

lastUsedAt is latestTimestamp(device.lastUsedAt, device.lastBootedAt), read from each simulator's device.plist. A simulator that has never been booted has neither key, so it gets undefined and is filtered out. On a fresh runtime install, every simulator is new, so the list is empty.

platformSetupStatus in apps/web/src/components/device/DeviceSetup.tsx then sees iOS as available but sees no iOS devices, and shows the "install a runtime" message. That message sends the user to install something that is already installed.

main still pins DEVICE_HUB_VERSION = "0.12.0" (apps/server/src/device/DeviceToolchain.ts), so this is not fixed in a newer release.

Possible fixes:

  • Do not drop never-booted simulators in the hub list (or let T3 ask for them).
  • Or, in T3, if simctl has available devices but the hub returns none, show "boot a simulator once" in place of "install a runtime".

Steps to reproduce

  1. On macOS, install Xcode and an iOS runtime. Do not boot any simulator. (Or run xcrun simctl create test "iPhone 17" and never boot it.)
  2. Make sure no simulator device.plist under ~/Library/Developer/CoreSimulator/Devices/ has lastBootedAt / lastUsedAt.
  3. In the T3 Code desktop app, open Set up devices → Simulators.
  4. Result: "Xcode is installed, but no iOS Simulator is available."
  5. Run xcrun simctl boot "iPhone 17" && xcrun simctl shutdown "iPhone 17", then click Check again. The booted simulator should now show.

Version

0.0.45 (desktop app, T3 Code (Alpha)), expo-device-hub 0.12.0

Environment

macOS 27.0 (Darwin 27.0.0, arm64), Node v26.8.2, Xcode with iOS 27.0 runtime (24A434), xcode-select -p = /Applications/Xcode.app/Contents/Developer

Evidence

shell
1$ xcrun simctl list runtimes
2iOS 27.0 (27.0 - 24A434) - com.apple.CoreSimulator.SimRuntime.iOS-27-0
3
4$ xcrun simctl list devices available # 11 devices, all Shutdown
5 iPhone 18 Pro (06D7FCB6-...) (Shutdown)
6 iPhone 17 (67ADAA34-...) (Shutdown)
7 ...
8
9$ curl -s http://127.0.0.1:<hub-port>/api/devices
10{"simulators":[],"emulators":[],"errors":[{"message":"[android-utils] Failed to run `avdmanager list avd`:", ...}]}
11# (Android error is expected: no Android SDK on this machine. No iOS error.)
12
13$ for d in ~/Library/Developer/CoreSimulator/Devices/*/device.plist; do plutil -p "$d" | grep -iE 'lastBoot|lastUsed'; done
14# (no output: no simulator has ever been booted)

#11713 shows the same message, but its cause is xcode-select pointing at Command Line Tools (simctl fails). Here xcode-select points at Xcode and simctl works; the hub filters the devices out. Not a duplicate.

Fix applied or workaround

Workaround: boot each simulator you want once (xcrun simctl boot "iPhone 17", or open Simulator.app), then click Check again.

Filed by

Claude Code (claude-opus-5-5) via t3 triage

Note

Grok responding on behalf of Julius.

Triage

Thanks for the clear repro and the pointer into listIosSimulators, @maciekzygmunt. This matches current main, and it is not a duplicate of #11713.

What I found

  • GET /api/devices on the pinned hub (expo-device-hub 0.12.0, DEVICE_HUB_VERSION in apps/server/src/device/DeviceToolchain.ts) is a recents list, not a full inventory. listIosSimulators keeps a simulator only when its device.plist has lastUsedAt or lastBootedAt (.filter((device) => device.lastUsedAt !== undefined)).
  • A simulator that has never been booted has neither date, so it is dropped and the hub returns "simulators": [] with no iOS error. That matches this report. The same filter is still in expo-device-hub 0.15.0 and in 0.16.0-canary-20261005-e066832, and the hub documents it as intentional ("simulators with plist usage history"). Bumping the pin will not fix it.
  • T3 then misreads that empty list. platformSetupStatus in apps/web/src/components/device/DeviceSetup.tsx treats "iOS available and no iOS devices" as "install a runtime." Availability only means xcrun is on PATH (LocalDeviceHost.platformReason), which is true here.
  • Android does not have this hole: DeviceService.fetchDevices merges emulator -list-avds, and the test "explicit setup discovers never-booted AVDs" covers shut-down AVDs the hub omitted. There is no simctl list equivalent for iOS.
  • #11713 / #14640 is the other way to hit the same sentence (xcode-select pointing at Command Line Tools, so simctl fails). That change still leaves this case broken, because simctl succeeds and the devices exist.

The workaround in the report is right: boot the simulator once so the plist gets a timestamp, then click Check again.

Likely fix area

  • List never-booted iOS simulators the way never-booted AVDs are listed (for example by merging a simctl list inventory when the hub returns none).
  • Do not tell the user to install a runtime when simctl already has available devices; a clearer message such as "boot a simulator once" would fit this case better.

A maintainer will decide on the fix direction.

Checked current main at a12d6ed942 on a Linux x86_64 triage VM. Running xcrun simctl list devices --json exits 127 (xcrun: command not found), so this environment cannot reproduce the macOS simulator setup flow.

Current main pins expo-device-hub 0.15.3 rather than the reported 0.12.0. Does this still happen on a current build with a never-booted simulator? Please include the T3 version, hub version, xcrun simctl list devices --json, and the hub's /api/devices response for that same host. The existing report is detailed; the remaining gap is whether the newer hub still filters these devices. Leaving this open pending macOS verification.

Synced from GitHub

Verified on macOS, answering the request for a current build with a never-booted simulator.

Environment: macOS 27.0.1 arm64, Xcode 27.1, T3 Code Nightly 0.1.4-nightly.20261027.3783, expo-device-hub 0.19.0-canary-20261023-1357114. Runtimes iOS 27.0 and 27.1 are installed.

Still reproduces on this hub. xcrun simctl list devices shows 12 available iOS simulators, all Shutdown. The hub's GET /api/devices returned simulators: 0, emulators: 0 and no iOS error. Its only error was an unrelated Android one (avdmanager failing on an x86_64 JDK). Settings → Devices then showed "Xcode is installed, but no iOS Simulator is available. Install a runtime in Xcode Settings → Components." even though the runtime was installed.

Screenshots:

  1. Settings → Devices in T3 Code shows the error even though the iOS runtime is installed:
  1. Xcode Settings → Components shows iOS 27.1 installed:
  1. xcrun simctl lists 2 iOS runtimes and 12 available simulators, all Shutdown. (iPhone 17 and iPhone Air had each been booted once during my testing before this print was taken, so the hub lists those two. The other 10 were never booted.)

Cause confirmed in the bundled hub: listIosSimulators ends with .filter((device) => device.lastUsedAt !== undefined), so a simulator with neither lastUsedAt nor lastBootedAt in its device.plist is dropped. Refresh and toggling Device hub off/on did not help. After I booted one simulator once, the hub listed exactly that one (['iPhone 17']) and the other 11 stayed hidden.

Checked #24027 against this machine. I merged current main into it locally. It conflicts with #25815 (androidAvailable vs availablePlatforms) in DeviceHubOperations.ts and DeviceService.test.ts. I resolved that by deriving androidAvailable from availablePlatforms.android and having the test fixture read its Ref through availablePlatforms.

  • Ran the merged makeDeviceHubOperations against the real running hub with real xcrun: the hub reported 1 simulator, the merged list had 12 with no duplicate ids, including the never-booted iPhone 18 Pro and the iOS 27.1 iPhone Duo, and detail was empty.
  • Negative control: with the available.ios block disabled, the 5 PR regression tests (local and SSH host, plus the three simctl failure cases) failed, and so did the real-machine test. They pass with it enabled.
  • Booting a never-listed UDID through the hub (POST /api/devices/boot, then /api/devices/shutdown) worked, and the simulator then appeared in the hub's own list.
  • apps/server/src/device: 15 files, 132 tests passing. check:changed format, lint, knip, GraphQL and typecheck (36 packages) all passed.

Not verified: the check:changed test step failed here because Playwright's Chromium isn't installed on this machine (browser tests can't launch), and I did not get a complete run of the full suite. I did not test the SSH path on a real remote Mac or the UI itself.

The workaround ("boot the simulator once") is still right until a fix like #24027 lands.

Synced from GitHub

This also reproduces on the stable 0.1.3 release (desktop app, T3 Code (Beta)), not only on nightly. It happened on a fresh install today, so every first-time user with a new Xcode setup is likely to see it.

Environment: macOS 27.0.1 (26A434) arm64, Xcode 27.1 (27A9275), iOS 27.0 and 27.1 runtimes, expo-device-hub 0.19.0-canary-20261023-1357114 (the version v0.1.3 pins; main pins the same), agent-device 0.21.31.

  • xcrun simctl list devices available: 12 iOS simulators, all Shutdown, none ever booted. None of their device.plist files has lastUsedAt or lastBootedAt.
  • Hub GET /api/devices returned {"simulators":[],"emulators":[],"errors":[...]}. The only error was an unrelated Android avdmanager ENOENT.
  • Settings → Devices showed "Xcode is installed, but no iOS Simulator is available. Install a runtime in Xcode Settings → Components."
  • In the same session, agent-device devices found the simulators. Only the hub's list is empty.

Workaround confirmed: after each simulator was booted once (xcrun simctl boot <udid>, xcrun simctl bootstatus <udid>, xcrun simctl shutdown <udid>), the hub listed all 12 simulators where it had listed 0, including the iOS 27.1 one.

+1 for landing #24027. Even without it, changing the "install a runtime" message to say "boot a simulator once" would save new users from reinstalling a runtime they already have.

Filed by Claude Code (claude-opus-5-5) via t3 triage

Synced from GitHub

Confirmed this still happens with expo-device-hub 0.19.1: a newly created Simulator exists on the Mac, but T3’s device list hides it until it has been used.

Both PRs fix this by adding the missing Simulators to the list. Neither boots them automatically.

  • #24027 asks Apple’s Simulator tools directly. T3 then filters and combines the results. This gives T3 direct control over discovery and errors, but adds more discovery logic for T3 to maintain.
  • #28915 asks the device hub for its existing, more complete list. This reuses the hub’s rules about which devices are available and which a session may access. The trade-off is relying on that hub API continuing to behave as expected.

I prefer #28915 because the hub already has the information T3 needs. #24027 is a reasonable alternative if maintainers prefer T3 to control discovery directly.

Our regression tests and CI pass. We also confirmed that the fix finds a real never-used Simulator while preserving session restrictions. Actual booting and Device panel interaction remain untested.

Synced from GitHub

Note

Claude Code (claude-opus-5-5) responding on behalf of Paul.

Also happens on an SSH device host (a remote Mac), which the reports above hadn't covered.

Environment: T3 Code (Beta) 0.1.7 on macOS. Device host: Mac mini M2 over SSH, macOS 26.7.2, Xcode 26.4 with the iOS 26.4 runtime, expo-device-hub 0.19.1 started there by T3.

  • On the host, xcrun simctl list devices available shows 3 simulators, all Shutdown and never booted. None of their device.plist files has lastUsedAt or lastBootedAt.
  • device_list reported the host ready and iOS available: true, with 0 devices and no error. The remote hub's GET /api/devices returned "simulators": []; its only error was the unrelated Android avdmanager ENOENT.
  • T3 starts the remote hub with --hide-boot-device, so nothing in T3 could boot them. The workaround needed a shell on the remote Mac: xcrun simctl boot <udid> && xcrun simctl shutdown <udid>. That wrote lastBootedAt right at boot, and all 3 showed up in device_list straight away, without a hub restart.

On a remote host this is the normal state right after setup (Xcode's default simulators have never booted), and an agent working there sees nothing it can open. Whichever fix lands should cover SSH hosts too; #24027's regression tests include one.

Synced from GitHub

Thanks for confirming the SSH case. #28915 also covers SSH hosts: it queries the remote hub through T3’s existing SSH connection. Regression tests cover discovery and tools becoming available after connecting. Your report adds real-world evidence of the failure; we still haven’t verified our fix end to end on a remote Mac.

Synced from GitHub

Reproduced on 0.1.8-nightly.20261106.4275 with an SSH device host (macOS, Xcode 27.0). xcrun simctl list devices available showed 25 available simulators: 11 on iOS 27.0 and 14 on iOS 26.3. device_list for that host returned only one, a previously used iPhone 17 Pro on iOS 26.3. None of the iOS 27.0 devices appeared until one was booted out-of-band with simctl boot. After that, device_open and the agent-device runner worked normally. So users and agents conclude that only older runtimes exist, even though the newer ones are installed and work.