claude-code - 💡(How to fix) Fix [FEATURE] Wire `sandbox.allowPty` from settings.json into the macOS Seatbelt profile (sandbox-runtime already supports it)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

This matters because on macOS, R (Rscript) busy-spins at ~100% CPU when run without a controlling terminal, and the reliable workaround is a pty wrapper (script -q /dev/null Rscript … < /dev/null). Since openpty is blocked in-sandbox, every pty-wrapped job must run with dangerouslyDisableSandbox: true — the entire job leaves the sandbox to obtain one pty device pair. The same applies to anything else that needs a pty (expect scripts, tools that insist on a tty).

Fix Action

Fix / Workaround

This matters because on macOS, R (Rscript) busy-spins at ~100% CPU when run without a controlling terminal, and the reliable workaround is a pty wrapper (script -q /dev/null Rscript … < /dev/null). Since openpty is blocked in-sandbox, every pty-wrapped job must run with dangerouslyDisableSandbox: true — the entire job leaves the sandbox to obtain one pty device pair. The same applies to anything else that needs a pty (expect scripts, tools that insist on a tty).

RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing requests and this feature hasn't been requested yet
  • This is a single feature request (not multiple features)

Problem Statement

Pty allocation is always blocked inside the macOS sandbox: script -q /dev/null true fails with script: openpty: Operation not permitted, and python3 -c 'import pty; pty.openpty()' fails with OSError: out of pty devices.

This matters because on macOS, R (Rscript) busy-spins at ~100% CPU when run without a controlling terminal, and the reliable workaround is a pty wrapper (script -q /dev/null Rscript … < /dev/null). Since openpty is blocked in-sandbox, every pty-wrapped job must run with dangerouslyDisableSandbox: true — the entire job leaves the sandbox to obtain one pty device pair. The same applies to anything else that needs a pty (expect scripts, tools that insist on a tty).

The embedded @anthropic-ai/sandbox-runtime already supports exactly this: its config schema has allowPty ("Allow pseudo-terminal (pty) operations (macOS only)"), and when set, the Seatbelt profile generator emits (allow pseudo-tty) plus read/write/ioctl on /dev/ptmx and /dev/ttys*. But there is no way to reach it from Claude Code (inspected in 2.1.165):

  • The settings.json sandbox schema has no allowPty field — and it ends with .passthrough(), so setting "sandbox": { "allowPty": true } is accepted silently and does nothing.
  • The settings→runtime conversion builds the runtime config from a fixed key list (network, filesystem, ignoreViolations, enableWeakerNestedSandbox, enableWeakerNetworkIsolation, ripgrep, seccomp, bwrapPath, socatPath) and never copies allowPty, so the value never reaches the profile generator (which reads perCallOverride?.allowPty ?? runtimeConfig?.allowPty).

A sandbox-exec A/B on Darwin 24.6 confirmed the emitted Seatbelt block is necessary and sufficient: replaying the sandbox's deny-default profile reproduces the openpty failure, and appending the allowPty block fixes it.

Proposed Solution

Wire sandbox.allowPty through from settings.json:

  1. Add allowPty: boolean to the settings.json sandbox schema (macOS only, matching the runtime's own description).
  2. Copy it in the settings→runtime conversion — appears to be a one-line change, since runtime support is complete.
  3. Document it on the sandboxing settings page.

Expected result: with "sandbox": { "allowPty": true }, script -q /dev/null true < /dev/null exits 0 inside the sandbox.

Alternative Solutions

  • excludedCommands: ["script *"] — rejected: script runs arbitrary commands, so this is a wrap-anything sandbox escape, strictly worse than the requested grant.
  • Per-invocation dangerouslyDisableSandbox: true — the status quo; runs the entire job unsandboxed to obtain one device pair.
  • Wrapping commands in the standalone srt CLI with an allowPty config — Seatbelt can't nest (sandbox_apply: Operation not permitted in-sandbox), so this also requires disabling Claude Code's sandbox for the call, plus maintaining a parallel policy by hand.

Priority

High - Significant impact on productivity

Feature Category

Configuration and settings

Use Case Example

  1. A statistical analysis project runs R model fits from Claude Code (Rscript code/fit.R).
  2. On macOS, R busy-spins without a controlling terminal, so the job is launched as script -q /dev/null Rscript code/fit.R < /dev/null.
  3. In-sandbox this fails immediately with script: openpty: Operation not permitted, so today the call must set dangerouslyDisableSandbox: true, forfeiting filesystem and network confinement for the whole job.
  4. With sandbox.allowPty: true, the same job would run fully sandboxed (filesystem + egress bounds intact) with only the pty device pair added.

Additional context: tested on Claude Code 2.1.165 (native install), macOS Darwin 24.6. The grant is bounded — (allow pseudo-tty) + /dev/ptmx + /dev/ttys* (the /dev/ttys* regex does match other live same-user terminal ttys; that trade-off may be worth a sentence in the docs). For comparison, other agent CLI sandboxes (e.g. OpenAI Codex CLI's macOS base policy) ship (allow pseudo-tty) + /dev/ptmx access by default.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING