CLAUDE_CODE_TMPDIR not respected for CWD tracking files on multi-user servers

Resolved 💬 4 comments Opened Mar 5, 2026 by ShonaDouglasKT Closed Mar 9, 2026

Description

On shared multi-user Linux servers, Claude Code's CWD tracking files (/tmp/claude-<hex>-cwd) accumulate in /tmp/ and collide across users, causing "Permission denied" errors in stderr. The CLAUDE_CODE_TMPDIR environment variable does not fix this — it is only partially respected.

Steps to reproduce

  1. Set up a shared Linux server with multiple users running Claude Code
  2. Set CLAUDE_CODE_TMPDIR=/var/tmp/claude (or any custom path)
  3. Have multiple users run Claude Code sessions over time
  4. Observe /tmp/claude-*-cwd files accumulating, owned by different users
  5. Eventually a session gets a hex ID that collides with an existing file owned by another user → EACCES

Expected behaviour

CLAUDE_CODE_TMPDIR should control where CWD tracking files are written, and they should be scoped per user (e.g. by UID) to prevent cross-user collisions.

Actual behaviour

CWD tracking files ignore CLAUDE_CODE_TMPDIR and always write to /tmp/claude-<hex>-cwd with no user scoping.

In the minified source (v2.1.68), the CWD file path depends on whether sandbox mode is enabled:

  • Sandbox path (useSandbox=true): join(sandboxTmpDir, "cwd-<id>")sandboxTmpDir is built from CLAUDE_CODE_TMPDIR and includes a claude-<uid> subdirectory. This works correctly.
  • Non-sandbox path (default): join(os.tmpdir(), "claude-<id>-cwd") — uses bare os.tmpdir() (/tmp), ignoring CLAUDE_CODE_TMPDIR entirely, with no user scoping.

The hex ID is only 4 digits (Math.floor(Math.random()*65536).toString(16).padStart(4,"0")), giving 65,536 possible values. On our server with 9 users, we have 2,392 accumulated files and collisions are routine.

Other temp paths (e.g. the Fm() helper function) correctly respect CLAUDE_CODE_TMPDIR and include user scoping via claude-<uid> subdirectories.

Suggested fix

The non-sandbox CWD file path should use CLAUDE_CODE_TMPDIR and include user scoping, consistent with how other temp paths are handled:

// Current (broken for non-sandbox)
cwdFilePath = join(tmpdir(), `claude-${id}-cwd`)

// Suggested fix
cwdFilePath = join(process.env.CLAUDE_CODE_TMPDIR || tmpdir(), `claude-${process.getuid()}-claude-${id}-cwd`)

Or reuse the existing Fm() helper that already handles this correctly.

Environment

  • Claude Code version: 2.1.68
  • OS: Debian Linux 6.1.0-40-amd64 (shared server, 9 users)
  • CLAUDE_CODE_TMPDIR=/var/tmp/claude (set but not respected for CWD files)

Additional context

Full analysis with source code references: https://gist.github.com/ShonaDouglasKT/e00d60776bd69a91b4a97ca62fe06545

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗