preview_start fails with spawn disclaimer ENOENT on macOS 26.2 (embedded claude-code 2.1.111)

Resolved 💬 1 comment Opened Apr 24, 2026 by mnease Closed May 28, 2026

Summary

mcp__Claude_Preview__preview_start consistently fails with:

spawn /Applications/Claude.app/Contents/Helpers/disclaimer ENOENT

…even though that binary exists, is correctly codesigned, and runs fine when invoked directly from a shell. Reproducible across every .claude/launch.json config I've tried (python3, npx, npm), regardless of whether runtimeExecutable is a PATH-resolved command or an absolute path, regardless of cwd.

Environment

  • macOS: 26.2 (25C56), arm64
  • Claude Desktop: 1.3883.0
  • Embedded claude-code bundle: 2.1.111 (at ~/Library/Application Support/Claude/claude-code/2.1.111/)
  • Native claude-code CLI: 2.1.119 (at ~/.local/bin/claude, separate install, not what's running here — CLAUDE_CODE_ENTRYPOINT=claude-desktop)

Reproduction

Any .claude/launch.json entry will do. Example:

{
  "name": "static-server",
  "runtimeExecutable": "/opt/homebrew/bin/python3",
  "runtimeArgs": ["-m", "http.server", "4545"],
  "port": 4545,
  "cwd": "."
}

Call preview_start → ENOENT.

Diagnostics gathered

disclaimer binary is healthy:

  • Exists: -rwxr-xr-x 135600 bytes at /Applications/Claude.app/Contents/Helpers/disclaimer
  • file: Mach-O universal binary with 2 architectures: [x86_64] [arm64]
  • codesign -dvvv: signed by Anthropic PBC (Team Q6L2SF6YDW), hardened runtime enabled, Developer ID Application cert, Apple-timestamped 2026-04-21.
  • otool -L: only dep is /usr/lib/libSystem.B.dylib
  • Direct exec works: disclaimer /bin/echo hellohello
  • xattr: only com.apple.provenance / com.apple.macl — nothing quarantine-related

Claude Desktop / Claude Code entitlements:

Neither has com.apple.security.app-sandbox. Claude Code's entitlements are just cs.allow-jit, cs.allow-unsigned-executable-memory, cs.disable-library-validation, device.audio-input. So classic App Sandbox denial is out.

Possibly related (but not the root cause): the embedded claude.app bundle at ~/Library/Application Support/Claude/claude-code/2.1.111/claude.app/Contents/ ships without a Helpers/ subdirectory:

Contents/
├── CodeResources
├── Info.plist
├── MacOS
└── _CodeSignature    ← no Helpers/

Deleting the 2.1.111/ directory and letting Claude Desktop re-download produces the same bundle layout. Symlinking /Applications/Claude.app/Contents/Helpers into the embedded bundle at Contents/Helpers does not change the preview_start error — preview_start already hardcodes the absolute /Applications/Claude.app/... path, so the embedded Helpers absence is likely a red herring.

Why it looks like a sandbox-masked denial

ENOENT on spawn() when the file demonstrably exists, is executable, and is invokable from a normal shell is the classic fingerprint of macOS kernel-level exec denial (TCC, sandbox, or entitlement mismatch) — macOS masks the real error as ENOENT. But none of the visible entitlements explain which policy would be denying it.

What I'd expect the fix to look like

Either:

  1. preview_start shouldn't be spawning via the desktop-app-bundle disclaimer at all from the embedded claude-code — it should use a helper bundled alongside the claude-code binary (which means shipping Helpers/ inside the embedded bundle).
  2. Or the disclaimer call path needs an env/entitlement adjustment so the cross-bundle exec actually resolves.

Workaround

For pre-commit visual spot-checks, dropping to bash + python3 -m http.server on a temp fixture dir + manual browser load. Works but loses preview_screenshot / preview_inspect.

Happy to share more

launch.json content, full error traces, codesign dumps, dtruss of a failing spawn, etc. Let me know what would help.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗