Skip to main content

Troubleshooting

Start with turnstile doctor. It checks each link from your shell to the daemon and prints a fix for anything broken.

Commands aren't being gated​

Usually something reordered PATH after turnstile's block, such as a version manager initialized late in .zshrc. doctor names the tools that resolve around the shims. Rerun turnstile init, which moves its block to the end of your startup files.

In zsh and bash, the block also moves the shims back to the front before each prompt, so nvm use or mise activate can't leave them behind. If your startup files predate that hook, rerun turnstile init to get it.

Calls by absolute path, such as /usr/bin/make, always go around the shims. See limits.

Commands in a sandboxed agent run ungated​

Agent sandboxes can block the daemon's socket, so gated commands there run ungated and print can't reach the daemon from inside this sandbox. Allow only the exact socket reported by doctor if the harness supports it. A turnstile run wrapper can't grant that permission. See Codex command and socket integration for supported invocation forms, permission requirements, and unavailable-harness diagnostics. turnstile never starts the daemon from inside a sandbox, so it can't pick up the sandbox's limits. Run turnstile doctor once from an ordinary shell to start it.

Status says observation is blocked​

Use status or doctor for these diagnostics. The current top and menu bar missing-snapshot displays do not distinguish connection errors.

permissionDenied means the process could not inspect the daemon endpoint. It does not mean the daemon stopped. Grant the active harness permission to connect to the exact Unix socket printed by status or doctor, then retry in a new session. Keep the rest of the sandbox policy intact. If the harness has no socket permission control, its owner must provide one; a command wrapper cannot fix this.

unreachable means the endpoint could not be queried or did not return a status reply. Check the reported error and daemon log from a permitted terminal before restarting anything. absent means there was no listener at the reported endpoint at the time of the check. Memory marked unknown includes failed or unsupported sysctl reads; repeat the diagnostic from a permitted terminal instead of treating the scheduler's fallback values as measured capacity.

Checking other shells​

turnstile doctor --shells runs zsh -c, zsh -lc, zsh -ic, and bash -lc from a bare environment and reports any shell where a tool resolves ahead of the shims. It also checks the newest Claude Code shell snapshot: a session that started before turnstile init keeps the PATH it had, so its commands run ungated until you start a new one.

init installs startup blocks for the selected shell only (by default, $SHELL). If your terminal uses zsh but your agent also starts bash, run turnstile init --shell bash from an ordinary terminal, then start a new agent session. This updates .bashrc and the first readable login file among .bash_profile, .bash_login, and .profile, creating .bash_profile only if none exists. Bash -c does not read those files; its parent must supply the shim PATH, or explicitly evaluate turnstile env --shell bash before starting commands.

Finding runs that went ungated​

When a gated command can't reach the daemon it runs anyway, and records the run in ~/.turnstile/ungated.log: the time, the cause, the directory, and the command. turnstile doctor reports the last day's count and the most recent cause.

A job is waiting longer than expected​

turnstile status shows what's running and why each job waits. turnstile bump <job> moves one to the front.

Something's wrong and you need to work now​

turnstile disable turns gating off everywhere at once. turnstile enable turns it back on. For a single shell, set TURNSTILE_DISABLE=1.

Logs​

The daemon logs to ~/.turnstile/daemon.log. Output from runs without a terminal is kept in ~/.turnstile/logs for a day, or a week if the run failed. Read it with turnstile logs <job>, or right-click the job in the menu bar app.

Scheduler stalls (unreleased)​

When a scheduler tick completes, the daemon logs ticks lasting at least five seconds with their slowest phase. It also logs gaps of at least 30 seconds between ticks:

daemon tick took 6s; slowest phase: escape scan (6s)
daemon scheduler had a 2m15s gap between ticks

Elapsed times use a monotonic clock. The phases cover memory observation, config reload, job monitoring, escape scans, scheduling, and history and log maintenance. A phase's elapsed time can include delays in scheduling the daemon. A gap can include work in other daemon callbacks. These messages help locate a stall; they don't establish its cause.