Skip to content

[BUG] Claude Code fails with EACCES on multi-user systems when /tmp/claude is not writable #20139

Description

@noor01

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

Claude Code fails with EACCES on multi-user systems when /tmp/claude is not writable

Environment

  • OS: Linux (Ubuntu/Debian-based cluster)
  • Claude Code Version: 2.1.15 (linux-x64)
  • Installation method: VS Code extension
  • Environment type: Shared academic/research computing cluster with multiple users

Relevant environment variables:

TMPDIR=/tmp/user/457296
TEMPDIR=/tmp/user/457296
TMP=/tmp/user/457296
TEMP=/tmp/user/457296

File system state:

$ ls -ld /tmp
drwxrwxrwt 34 root root 4096 Jan 22 10:40 /tmp

$ ls -ld /tmp/claude
drwxr-xr-x 9 user2 operator 4096 Jan 15 23:35 /tmp/claude

$ whoami
user1

* user2 is another user. user1 is me. usernames changed for privacy

$ id
uid=457296(user1) gid=37(operator) groups=37(operator)

Problem Description

Claude Code attempts to create workspace-specific cache directories in /tmp/claude/ regardless of the TMPDIR environment variable setting. When /tmp/claude/ is owned by another user with restrictive permissions (755), Claude Code fails with an EACCES permission error.

This is a common scenario on shared computing clusters where:

  1. Multiple users run Claude Code
  2. The first user to run Claude Code creates /tmp/claude/ with their default umask (typically 755)
  3. Subsequent users cannot write to this directory even if they're in the same group

Actual Behavior

Error: EACCES: permission denied, mkdir '/tmp/claude/-path-to-project'

The error occurs when Claude Code tries to create a workspace-specific subdirectory in /tmp/claude/ by transforming the working directory path (e.g., /university_cluster/users/user1/projects/project1/tmp/claude/-university_cluster-user1-projects-project1).

Examples of failed operations:

  • Running Python scripts: python main.py configs/config.yaml
  • Spawning Task tool agents
  • Any operation requiring workspace cache

What Should Happen?

Expected Behavior

Claude Code should handle temporary directories in one of these ways:

Option 1: Respect TMPDIR (Preferred)

When TMPDIR is set, use $TMPDIR/claude/ instead of hardcoded /tmp/claude/:

TMPDIR=/tmp/user/457296 → Use /tmp/user/457296/claude/-path-to-project

Option 2: Graceful fallback

If /tmp/claude/ is not writable, fall back to:

  1. $TMPDIR/claude/ if TMPDIR is set
  2. $HOME/.cache/claude/ as a last resort

Option 3: User-specific directories by default

Always use user-specific paths to avoid multi-user conflicts:

/tmp/claude-${USER}/-path-to-project
or
${TMPDIR:-/tmp}/claude/${USER}/-path-to-project

Additional Context

Observed behavior:

  • Claude Code successfully creates user-specific session files in $TMPDIR/claude/ (e.g., /tmp/user/457296/claude/)
  • However, workspace caches are hardcoded to /tmp/claude/ regardless of TMPDIR
  • This inconsistency suggests the codebase uses different temp directory resolution in different modules

Current workarounds:

  1. Administrator runs: sudo chmod 1777 /tmp/claude (sticky bit like /tmp)
  2. First user runs: chmod 775 /tmp/claude (if they remember)
  3. Each user creates their own directory: mkdir ~/tmp/claude && ln -s ~/tmp/claude /tmp/claude-${USER} (doesn't fix the issue)

All workarounds are suboptimal and require knowledge of the internals.

Suggested Fix

The temp directory resolution should follow this precedence:

function getClaudeWorkspaceTmpDir(workspacePath) {
  const baseTmpDir = process.env.TMPDIR ||
                     process.env.TMP ||
                     process.env.TEMP ||
                     '/tmp';

  const claudeDir = path.join(baseTmpDir, 'claude');

  // Try to use shared directory, fall back to user-specific
  try {
    fs.mkdirSync(claudeDir, { recursive: true });
  } catch (err) {
    if (err.code === 'EACCES' || err.code === 'EPERM') {
      // Fall back to user-specific directory
      const userClaudeDir = path.join(os.homedir(), '.cache', 'claude');
      return path.join(userClaudeDir, sanitizeWorkspacePath(workspacePath));
    }
    throw err;
  }

  return path.join(claudeDir, sanitizeWorkspacePath(workspacePath));
}

Related Issues

This may be related to general temp directory handling in Claude Code. Consider auditing all temp directory creation to ensure consistent behavior across the codebase.

System Information

$ uname -a
Linux 5.15.0-116-generic

$ ls -la /tmp/user/457296/claude/
# Successfully creates files here - this works correctly

$ env | grep -i tmp
TEMPDIR=/tmp/user/457296
TMPDIR=/tmp/user/457296
TMP=/tmp/user/457296
TEMP=/tmp/user/457296

$ claude --version
2.1.15

Error Messages/Logs

Steps to Reproduce

Steps to Reproduce

  1. On a multi-user Linux system, have User A run Claude Code, which creates /tmp/claude/ with permissions drwxr-xr-x (755)
  2. As User B (different user, same group), set environment variables:
    export TMPDIR=/tmp/user/$(id -u)
    export TMP=$TMPDIR
    export TEMP=$TMPDIR
  3. Launch Claude Code from a project directory (e.g., /path/to/project)
  4. Attempt to run any command that triggers Claude Code's workspace cache

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

2.1.15

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions