iOS Simulator sidecar aborts on every frame render (SIGABRT in -[_MTLDevice recordBinaryArchiveUsage:])
Bug report — Claude Code iOS Simulator: sidecar aborts on every frame render
Claude 1.40609.0 · macOS 27.0 (26A5378j) · Xcode 27.0 (27A5252f) · Hardware M5 Max (J716c)
Summary
The claude-ios-sim helper crashes with SIGABRT on every frame render. attach and all
input actions work — tap lands and changes app state on both an iPhone 17 Pro and an Apple
Watch Ultra 3 — but screenshot and the panel's live video stream kill the sidecar within ~1s.
The panel therefore opens and immediately dies (failed to open frame stream), and every
subsequent tool call returns the "restarting after a crash" backoff.
Deterministic: 14 crash reports in a 5-minute window, zero successful frame renders ever on
this machine.
Crash
~/Library/Logs/DiagnosticReports/Retired/claude-ios-sim-2026-08-29-*.ips
NSInvalidArgumentException: -[__NSPlaceholderArray initWithObjects:count:]:
attempt to insert nil object from objects[0]
__39-[_MTLDevice recordBinaryArchiveUsage:]_block_invoke
_dispatch_once_callout
-[_MTLDevice recordBinaryArchiveUsage:]
-[_MTLBinaryArchive loadFromURL:error:]
-[_MTLBinaryArchive initWithOptions:device:url:error:]
+[CIKernelLibrary(Internal) internalBinaryArchiveWithName:device:]
CI::PrecompiledUberFunctions::PrecompiledUberFunctions(CI::MetalContext const*)
CI::new_uber_functions(CI::MetalContext const*)
CI::MetalContext::init(id<MTLCommandQueue>, char const*)
+[CIContext(Internal) internalContextWithMTLCommandQueue:options:]
+[CIContext contextWithMTLDevice:options:]
FBSurfaceImageGenerator.image()
FBSimulatorImage.image()
What objects[0] is
Disassembly of __39-[_MTLDevice recordBinaryArchiveUsage:]_block_invoke:
+48 ldr x0, [x8, #0x230]
+56 bl <objc_msgSend stub> ; [NSBundle mainBundle]
+60 bl <objc_msgSend stub> ; -bundleIdentifier
+64 cbz x0, <+268> ; GATE: nil bundle id -> early exit
+72 ldr x8, [x8, #0x150]
+76 cmn x8, #0x1
+80 b.ne <+316> ; GATE: only on the uninitialised sentinel
+88 ldr x8, [x8, #0x128] ; objects[0] = shader cache path <-- NIL
+104 add x9, x9, #0x7a8 ; objects[1] = @"archiveUsage.db"
+108 stp x8, x9, [sp, #0x18]
+124 mov w3, #0x2 ; count = 2
+128 bl +[NSArray arrayWithObjects:count:] ; throws
getShaderCachePath.cold.1 is the immediately adjacent symbol in Metal's __text, naming the
producer of objects[0]. Metal is building @[getShaderCachePath(), @"archiveUsage.db"] and
the shader-cache path is nil.
Likely cause — claude-ios-sim.sb denies the Metal shader cache
Metal's shader cache lives in $DARWIN_USER_CACHE_DIR/com.apple.metal — the C/ directory.
The profile allows writes only under CORESIM_HOME, CORESIM_LOGS, DARWIN_TMP (the T/
dir) and /private/var/tmp. Probed directly under the shipped profile:
$ sandbox-exec -f claude-ios-sim.sb <real params> touch $DARWIN_USER_CACHE_DIR/com.apple.metal/.probe
touch: .../C/com.apple.metal/.probe: Operation not permitted # exit 1
$ sandbox-exec -f claude-ios-sim.sb <real params> touch $DARWIN_USER_TEMP_DIR/.probe
# exit 0
Candidate one-line fix:
(allow file-write* (subpath (param "DARWIN_CACHE"))) ; scoped to com.apple.metal
Confidence: strongly supported, not formally closed. No local repro reaches the crash
state, because the +64 gate requires a non-nil mainBundle.bundleIdentifier and the+80 gate requires a still-uninitialised sentinel (first call only). The last link — that the
write-deny is what makes getShaderCachePath() return nil — is inference from the probe, not
a reproduction.
Ruled out (tested, not assumed)
- Missing version keys in the helper's
Info.plist. It genuinely has noCFBundleVersion
or CFBundleShortVersionString (the crash report shows Version: ???), but a repro bundle
with and without those keys behaves identically. Worth fixing for hygiene; not this bug.
user-preference-readdenials as the sole cause.sandboxdlogs three real ones
(kcfpreferencesanyapplication, com.apple.accessibility, com.apple.universalaccess), and
adding (allow user-preference-read) does not change the CIContext path on its own.
Suggested fixes, in priority order
- Stop routing frames through CoreImage.
FBSurfaceImageGeneratoruses aCIContext
only to turn an IOSurface into a CGImage. CGImageCreate over the locked surface, or
vImage, removes the whole Metal / CoreImage / binary-archive dependency and is faster.
- Catch it. This is an ObjC
NSInvalidArgumentExceptionand is catchable. Today one
Apple-side throw takes down the sidecar and detaches every device attached to it.
- Add a
simctl io <udid> screenshotstill-frame fallback. The helper already shells out
to simctl for boot / shutdown / probing, so the plumbing exists.
- Allow the Metal shader cache in the seatbelt profile (above).
Unrelated second defect — the setup wizard's Xcode check
On a Mac with only Xcode-beta.app and no /var/db/xcode_select_link, the wizard is stuck on
"Select Xcode" and tells the user to fix it in Xcode → Settings → Locations, where everything
already looks correct. xcode-select -p and Xcode's own picker both resolve correctly through
xcselect's single-Xcode fallback; only the link file is missing.
- The check reads
realpath('/var/db/xcode_select_link')alone, so it cannot see a working
fallback configuration.
- Its suggested fix path is hardcoded to
/Applications/Xcode.app. On a beta-only machine
that path does not exist, so xcodeSelectFixPath falls to "" and the UI renders the
"open Settings → Locations" copy — advice that cannot resolve the problem.
- The sidecar's
XCODE_APPsandbox param resolves as
realpath(link) ?? $DEVELOPER_DIR, also falling back to the literal /Applications/Xcode.app,
so process-exec would be scoped to a non-existent bundle.
Fix: derive both the check and the suggested command from xcode-select -p, and use the real
developer dir for XCODE_APP.
Workaround for users: sudo xcode-select -s /Applications/Xcode-beta.app/Contents/Developer.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗