iOS Simulator panel crash-loops on macOS 27 beta: seatbelt profile denies Metal's new cache-dir writes

Status Open
Maintainer reply None cached
Activity 3 comments · opened Aug 21, 2026

Environment

  • Claude desktop app 1.34493.1 (macOS), iOS Simulator panel (claude-ios-sim helper)
  • macOS 27.0 beta (26A5416b), Apple Silicon
  • Xcode 26.6 release selected (xcode-select), iOS 26.0 simulator runtime booted

Summary

On macOS 27 beta 26A5416b, the Claude Code iOS Simulator panel crashes within seconds of every attach — the sidecar exits, the supervisor relaunches it, and after a few cycles it gives up ("stopped retrying after repeated crashes"). The crash is an uncaught NSInvalidArgumentException inside Metal while CoreImage initializes, and the root cause is an interaction between the helper's seatbelt profile (Claude.app/Contents/Resources/claude-ios-sim.sb) and new Metal behavior in this macOS beta.

Crash signature

Every .ips for claude-ios-sim shows the same last-exception backtrace:

*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]

CoreFoundation   -[__NSPlaceholderArray initWithObjects:count:]
CoreFoundation   +[NSArray arrayWithObjects:count:]
Metal            __39-[_MTLDevice recordBinaryArchiveUsage:]_block_invoke
libdispatch      _dispatch_once_callout
Metal            -[_MTLDevice recordBinaryArchiveUsage:]
Metal            -[_MTLBinaryArchive loadFromURL:error:]
Metal            -[_MTLBinaryArchive initWithOptions:device:url:error:]
CoreImage        +[CIKernelLibrary(Internal) internalBinaryArchiveWithName:device:]
CoreImage        CI::PrecompiledUberFunctions::PrecompiledUberFunctions(CI::MetalContext const*)

It fires the moment the frame stream opens (first CoreImage/Metal context in the helper).

Root cause

On 26A5416b, Metal's recordBinaryArchiveUsage: writes usage data under DARWIN_USER_CACHE_DIR (/var/folders/.../C/) when CoreImage loads its precompiled kernel archive. The seatbelt profile only allows writes to the CoreSimulator dirs, DARWIN_TMP, and /private/var/tmp — the user cache dir is denied. The denied handle surfaces as a nil in an NSArray literal inside the dispatch_once block, and the helper aborts.

Verified by bisection with a minimal repro:

// ci_repro.swift — crashes under the profile, renders fine without it
import CoreImage
import Metal
let dev = MTLCreateSystemDefaultDevice()!
let ctx = CIContext(mtlDevice: dev)
let img = CIImage(color: .red).cropped(to: CGRect(x: 0, y: 0, width: 8, height: 8))
_ = ctx.createCGImage(img, from: img.extent)
print("CI+Metal render OK")
  • Run plain: renders OK.
  • Run via sandbox-exec -f claude-ios-sim.sb (same -D params the app passes, binary placed inside HELPER_BUNDLE so process-exec allows it): crashes with the exact signature above.
  • Same sandboxed run with one appended rule — (allow file-write* (subpath "<DARWIN_USER_CACHE_DIR>"))renders OK.

Narrower candidates were tested and do not fix it (so it's the cache-dir write, not a read): allowing full $HOME file-read-data doesn't help; CI_USE_SOFTWARE_RENDERER=1 doesn't help (the helper creates its CIContext with an explicit Metal device).

Suggested fix

Add the user cache directory to the profile's write allow-list, e.g.:

;; macOS 27 beta: Metal records binary-archive usage under the user cache dir
(allow file-write* (subpath (param "DARWIN_CACHE")))

with DARWIN_CACHE resolved from confstr(_CS_DARWIN_USER_CACHE_DIR) alongside the existing DARWIN_TMP param in the sidecar spawn code. Cache-dir scratch seems consistent with the profile's stated intent ("CoreSimulator device state and scratch only").

Notes for triage

  • The failure is invisible in the app UI beyond generic "sidecar exited (code=134)" / crash-loop messages; the .ips files carry the signature.
  • A second, related trap found while investigating: the profile's process-exec allow-list (helper bundle, /usr/bin, /usr/libexec, Xcode, /Library/Developer) means an exec of /bin/zsh etc. exits 71 with no crash log at all — worth a clearer supervisor message distinguishing "exec denied" from a real crash.
  • Local workaround applied on this machine: the one-line profile addition above (byte-exact original preserved). Happy to test a fixed build.

View original on GitHub ↗

3 Comments

CesarsPapa · 9 days ago

Follow-up — the crash is first-run-per-cache-lifetime, which will make it look intermittent in reports.

New differential findings (same machine, 26A5416b):

  • With the profile patched (cache-dir write allowed), the helper ran and Metal created $DARWIN_USER_CACHE_DIR/com.anthropic.claude.ios-sim/com.apple.metal{,fe}.
  • After that, reverting to the pristine profile no longer crashes — the panel streamed fine for 60s+. Once the per-app cache dirs exist, recordBinaryArchiveUsage: only needs reads, which the profile already allows.
  • Deleting $DARWIN_USER_CACHE_DIR/com.anthropic.claude.ios-sim and respawning under the pristine profile brings the crash back immediately (SIGABRT, exit 134, same stack).

So the nil comes specifically from the create path of Metal's per-app usage cache. Implications for triage:

  1. Users on macOS 27 beta crash-loop every launch — the sandboxed helper can never create the dir itself.
  2. Anything that seeds the dir out-of-band "fixes" it until macOS purges /var/folders caches (periodic maintenance, storage pressure, some updates) — then it returns with no apparent trigger. Expect confusing "it fixed itself / it broke again" reports.
  3. Ruled out as factors: installing Xcode's Metal Toolchain component (no effect either way), CI_USE_SOFTWARE_RENDERER, $HOME read allowances, and the top-level $DARWIN_USER_CACHE_DIR/com.apple.metal dir (repro unaffected by hiding it).

The suggested fix is unchanged (allow file-write* under the user cache dir, resolved via confstr(_CS_DARWIN_USER_CACHE_DIR)) — but a cheaper interim mitigation may be for the sidecar spawn code to mkdir -p the helper's per-app Metal cache dirs before applying the sandbox, which sidesteps the create path entirely without widening the profile.

CesarsPapa · 9 days ago

Confirmed the interim mitigation empirically: on a fully stock app (an auto-update overnight replaced our patched profile — signature seal intact, no local modifications), simply pre-creating the two empty directories

mkdir -p "$(getconf DARWIN_USER_CACHE_DIR)/com.anthropic.claude.ios-sim/com.apple.metal" \
         "$(getconf DARWIN_USER_CACHE_DIR)/com.anthropic.claude.ios-sim/com.apple.metalfe"

takes the panel from crash-loop (SIGABRT every frame-stream open) to stable 60s+ streaming. Empty dirs suffice — Metal only needed the create to succeed. So the sidecar spawn code doing this mkdir -p before applying the sandbox is a complete fix on 26A5416b, no profile widening required. Also a one-line user workaround for anyone hitting this on the macOS beta today.

bmc08gt · 6 days ago

Confirming on a different beta build and chip: macOS 27.0 (26A5353q), M3 Max (Mac15,10), iOS 27.0 runtime (24A5390f) booted.

Identical signature — NSInvalidArgumentException: attempt to insert nil object from objects[0] out of recordBinaryArchiveUsage: via CI::PrecompiledUberFunctions, on the com.facebook.FBSimulatorControl.BitmapStream queue. Two dozen .ips files over two days, all the same stack.

The mkdir -p mitigation works here on a stock app: every screenshot call failed with "restarting after a crash" before, stable streaming immediately after.

Two details consistent with your triage:

  • xcrun simctl io <udid> screenshot captured frames fine throughout the crash-loop, so CoreSimulator itself is healthy and the fault is confined to the helper.
  • Clearing the top-level $DARWIN_USER_CACHE_DIR/com.apple.metal changed nothing, matching your finding that it is not a factor.

Also worth noting for anyone landing here from a wedged-simulator symptom: none of the usual CoreSimulator resets help, since the helper is the thing crashing.