Skip to content

Build1 publisher2 min readPublished

Claude Code in print mode exits 0 after its sandbox denies a write to /tmp

Claude Code run with claude -p exits 0 when its directory sandbox blocks a write to /tmp, a developer's dev.to post shows. Scheduled agent jobs need an output path inside the agent's working directories and a check that the file exists.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying Claude Code in print mode exits 0 after its sandbox denies a write to /tmp
Generated illustration

What happened

  • Claude Code limits file edits to the directory it was launched from plus any explicitly added directories, and edits outside that set need approval.
  • In a terminal session the /tmp write raises a permission prompt the user approves, and a "don't ask again" answer is saved to local settings.
  • Denied in print mode, the model improvises by printing the report to stdout, writing a failure note, or summarizing what it would have saved.
  • A systemd unit with PrivateTmp=yes sends /tmp writes to a private namespace the user's shell cannot see, and the files are deleted when the service stops.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint A zero exit and a clean journal are not the success signal for a headless agent job, so the calling script has to test for the report file itself.
  • decision Choosing where output goes is a scope decision: a path under $HOME works only if it sits in the launch directory, is explicitly added, or is written by the shell.
  • exposure A job checked by hand in a terminal passes on an approval the timer run can never get, so a manual test gives a false pass for the scheduled run.

The author started from plain Unix. The script runs as the user, and /tmp is world-writable with the sticky bit. So any process running as that user should be able to create files there [14]. The job was a second-brain curator: a timer-driven script that has Claude sort session notes and write a curation report [2]. That holds for Unix, but Claude Code draws its own boundary around working directories [4].

The headless run enforces the same boundary as the terminal session. Under claude -p nobody is there to answer the prompt. The tool call is denied and the model is told the write was not allowed [6]. The model knows about the denial. The calling script never hears of it. "An agent that works around a denied permission looks like success to a shell script," the author wrote [8].

The broken script ran under set -euo pipefail, passed --allowedTools "Read,Glob,Grep,Write", and ended with echo "curator finished" [3]. Allowing the Write tool does not let it reach any path. The directory scope still applies [9]. With claude exiting 0 and the journal clean, strict mode had nothing to catch, and the echo was accurate [1].

/tmp stays a poor target even after the scope problem is fixed. On top of the private namespace a PrivateTmp=yes unit gets, /tmp is subject to systemd-tmpfiles cleanup [11].

The author summed up the fix as moving the logs out of /tmp and into $HOME/logs [16]. It has three parts: a permanent output location inside a directory Claude is allowed to write, log capture owned by the calling shell, and denials made visible [12]. The fixed script sets REPORT to $HOME/logs/sb-curator-out-$STAMP.md. It then runs cd "$HOME/second-brain", with the comment "working dir = what the agent may edit" [13].

$HOME/logs is not under $HOME/second-brain. By the post's own rule, an agent write there falls outside scope. It works only if that directory is explicitly added, or if the shell writes the file from the agent's output [1]. The quoted script stops at the claude -p call, so it does not show which route the author took [15].

I think the deny is the right default. An agent running with nobody watching should not be able to write everywhere its user can. The scoping rule gives it a boundary a reviewer can read off the launch directory and the added directories [4]. The cost lands on the caller. A zero exit from claude -p shows the process ran, even when the model fell back to stdout or a note [7].

What to watch

  • Whether Claude Code adds a non-zero exit code or a machine-readable denial record when a print-mode tool call is refused.
  • The rest of the author's fixed curator script, which would show how $HOME/logs enters the agent's scope and how denials are made visible.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories