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
- Set up a shared Linux server with multiple users running Claude Code
- Set
CLAUDE_CODE_TMPDIR=/var/tmp/claude (or any custom path)
- Have multiple users run Claude Code sessions over time
- Observe
/tmp/claude-*-cwd files accumulating, owned by different users
- 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
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. TheCLAUDE_CODE_TMPDIRenvironment variable does not fix this — it is only partially respected.Steps to reproduce
CLAUDE_CODE_TMPDIR=/var/tmp/claude(or any custom path)/tmp/claude-*-cwdfiles accumulating, owned by different usersEACCESExpected behaviour
CLAUDE_CODE_TMPDIRshould 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_TMPDIRand always write to/tmp/claude-<hex>-cwdwith no user scoping.In the minified source (v2.1.68), the CWD file path depends on whether sandbox mode is enabled:
useSandbox=true):join(sandboxTmpDir, "cwd-<id>")—sandboxTmpDiris built fromCLAUDE_CODE_TMPDIRand includes aclaude-<uid>subdirectory. This works correctly.join(os.tmpdir(), "claude-<id>-cwd")— uses bareos.tmpdir()(/tmp), ignoringCLAUDE_CODE_TMPDIRentirely, 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 respectCLAUDE_CODE_TMPDIRand include user scoping viaclaude-<uid>subdirectories.Suggested fix
The non-sandbox CWD file path should use
CLAUDE_CODE_TMPDIRand include user scoping, consistent with how other temp paths are handled:Or reuse the existing
Fm()helper that already handles this correctly.Environment
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