Hardcoded /tmp/claude paths break on Termux (Android)

Open 💬 16 comments Opened Dec 29, 2025 by renxida

Summary

Claude Code hardcodes /tmp/claude/ paths in multiple places, which fails on Termux (Android terminal emulator) where /tmp is not accessible due to Android's permission model.

Environment

  • Platform: Termux on Android
  • Claude Code version: latest npm install

Problem

The bundled cli.js contains hardcoded references like:

  • "/tmp/claude" for sandbox TMPDIR
  • "/tmp/claude_cli_latest_screenshot.png" for screenshots
  • "/tmp/claude/cwd-..." for working directory tracking
  • Various other /tmp/claude/ paths

On Termux, /tmp returns "Permission denied" because Android restricts access to system paths. Termux provides $TMPDIR pointing to /data/data/com.termux/files/usr/tmp which is the correct location.

Expected Behavior

Claude Code should respect the TMPDIR environment variable (or os.tmpdir() in Node.js) instead of hardcoding /tmp/claude.

Workaround

Currently patching cli.js with:

sed -i "s|/tmp/claude|/data/data/com.termux/files/usr/tmp/claude|g" cli.js

But this breaks on updates.

Suggested Fix

Replace hardcoded /tmp/claude with something like:

const CLAUDE_TMP = process.env.TMPDIR 
  ? path.join(process.env.TMPDIR, 'claude')
  : '/tmp/claude';

Or use Node's os.tmpdir() which already respects TMPDIR.

View original on GitHub ↗

16 Comments

github-actions[bot] · 6 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/15628

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

hah23255 · 6 months ago

See #15628 for comprehensive root cause analysis identifying 6 distinct hardcoded /tmp patterns in cli.js, including the critical sandbox TMPDIR override. The fix requires using os.tmpdir() instead of hardcoded paths.

salviz · 6 months ago

Additional Real-World Impact Report

Environment: Termux on Android (claude-code CLI)
Date: 2026-01-12

Issue Confirmed

Can confirm hardcoded /tmp/claude/ paths completely block SSH command execution on Termux:

EACCES: permission denied, mkdir '/tmp/claude/-data-data-com-termux-files-home/tasks'

Reproduction Steps

  1. Run Claude Code CLI in Termux
  2. Execute ANY command involving SSH via gcloud:

``bash
gcloud compute ssh instance --zone=us-central1-a --command='...'
``

  1. Fails immediately with permission denied on /tmp/claude/ mkdir

Impact

  • Blocks all gcloud SSH commands (cannot access remote GCP instances)
  • Breaks background task system (cannot capture SSH output)
  • No workaround available without manual CLI.js patching (breaks on updates)

Suggested Fix

Replace hardcoded /tmp/claude with:

const CLAUDE_TMP = process.env.TMPDIR ? path.join(process.env.TMPDIR, 'claude') : '/tmp/claude';

This respects Termux's $TMPDIR=/data/data/com.termux/files/usr/tmp and fixes the issue universally.

Testing Environment Available

Can provide detailed logs/debugging if needed. Running c4d-highcpu-4 instance on GCP currently - unable to check status due to this bug.

salviz · 6 months ago

Suggested Solution Confirmed ✅

The fix proposed in this issue is correct. Using process.env.TMPDIR or Node's os.tmpdir() would resolve this.

Tested workaround (partial): Setting export TMPDIR="\$HOME/.cache/tmp" in .bashrc works for direct commands but does NOT fix background agents or long-running operations - Claude Code must be respecting hardcoded paths internally.

Recommendation: Claude Code should replace all hardcoded /tmp/claude references with:

const tmpDir = require('os').tmpdir();
const CLAUDE_TMP = path.join(tmpDir, 'claude');

This would:

  • ✅ Fix Termux/Android support
  • ✅ Fix restricted /tmp environments (containers, SELinux)
  • ✅ Respect user-configured TMPDIR globally
  • ✅ Work with background agents and long operations
samliddicott · 6 months ago

Even correctly determining the valid system TMPDIR is not sufficient to avoid clashes between different instances of Claude, all making the same type of temporary files.

So within the selected temp dir, Claude should make a session-based subdir

And maybe $XDG_RUNTIME_DIR (if set should) be the tempdir in which the session-based subdir is created, not simply require('os').tmpdir()

If this should be a fresh report, rather than direction on this fix, please forgive my igorance and give me a nudge.

salviz · 6 months ago

Status Update: Verified on Latest Claude Code v2.1.11

Test Environment

  • Claude Code Version: 2.1.11
  • Platform: Android 13 (aarch64)
  • Kernel: Linux 5.15.178-android13-8
  • Terminal: Termux (on Samsung Galaxy device)
  • Node.js: v25.2.1
  • Bash: 5.3.9

Findings

PARTIAL FIX CONFIRMED:

  • ✅ Direct Bash commands execute WITHOUT /tmp/claude errors
  • ❌ Agent spawning STILL FAILS with EACCES: permission denied, mkdir '/tmp/claude/-data-data-com-termux-files-home/tasks'

Test Results

Test 1: Direct Bash Commands
ffmpeg -loop 1 -i photo.jpg -i audio.wav ... output.mp4

Result: ✅ Works fine, no path errors

Test 2: Agent Spawning (Explore)
claude-code [uses Task tool internally]

Result: ❌ EACCES: permission denied, mkdir '/tmp/claude/-data-data-com-termux-files-home/tasks'

Test 3: Agent Spawning (General-purpose)

Result: ❌ Same error as Test 2

Root Cause

The update fixed direct command execution but did NOT fix the task spawning infrastructure. Agents require /tmp/claude/{workspace-hash}/tasks/ directory creation, which fails on Termux where /tmp lacks write permissions for non-root users.

Recommended Fix

Implement environment variable detection as suggested in this issue:

const tmpDir = process.env.TMPDIR || process.env.XDG_CACHE_HOME || '/tmp';
const tasksDir = path.join(tmpDir, 'claude', 'tasks');

Workaround (Current)

Users can work around agent failures by:

  1. Using direct Bash commands via Bash tool (bypasses agent system)
  2. Setting TMPDIR=~/tmp before launching Claude Code
  3. Running encode/process commands directly in terminal shell script

Blocking Issues

  • Agent-based workflows completely broken on Termux
  • Background task execution impossible
  • Multi-step task delegation via Task tool non-functional

This significantly impacts Termux/Android accessibility despite the Bash command fix.

HEYANGLI23 · 5 months ago

export CLAUDE_CODE_TMPDIR=$HOME/tmp/claude
source ~/.bashrc

mfyz · 5 months ago

@HEYANGLI23 It worked thanks 🙏

Edit: its inconsistent. Sometimes still getting the same spawn errors.

More importantly this change broke my slash commands. Claude cant recognize them. Even though I ask claude to debug, it checks and says everything looks right.

Anyway. An official and proper fix needed from claude team.

salviz · 5 months ago

Confirming: Hardcoded /tmp paths ignore all environment variables

Adding another data point - I have both suggested workarounds configured:

export TMPDIR="$HOME/.cache/tmp"        # Directory exists ✓
export CLAUDE_CODE_TMPDIR="$HOME/.claude-tmp"  # Directory exists ✓

Neither works. Claude Code still attempts to create /tmp/claude/-data-data-com-termux-files-home/tasks.

System Info

  • Node.js v24.13.0 (exceeds v22 requirement)
  • npm 11.8.0
  • Termux on Android 13

The Fix Needed

As you correctly identified, the fix should be:

const CLAUDE_TMP = process.env.TMPDIR 
  ? path.join(process.env.TMPDIR, 'claude')
  : '/tmp/claude';

Or simply use Node's os.tmpdir() which already respects TMPDIR.

Current Status

  • 8+ duplicate issues filed
  • All marked as duplicates but none resolved
  • Only working workaround: proot bind mount

Could the team prioritize this fix? It's a one-line change that would unblock all Termux/Android users.

---
Tested on Termux with TalkBack accessibility

tellang · 4 months ago

Working Workaround: Direct cli.js Patching (No proot overhead)

For anyone hitting this on Termux, here's a faster alternative to the proot bind mount:

1. Patch cli.js directly

CLAUDE_JS="$PREFIX/lib/node_modules/@anthropic-ai/claude-code/cli.js"
TERMUX_TMP="/data/data/com.termux/files/usr/tmp"

sed -i "s|/tmp/claude|${TERMUX_TMP}/claude|g" "$CLAUDE_JS"

2. Create a repatch script (survives updates)

cat > $PREFIX/bin/claude-patch << 'SCRIPT'
#!/data/data/com.termux/files/usr/bin/bash
CLAUDE_JS="$PREFIX/lib/node_modules/@anthropic-ai/claude-code/cli.js"
TERMUX_TMP="/data/data/com.termux/files/usr/tmp"
if grep -q '"/tmp/claude' "$CLAUDE_JS" 2>/dev/null; then
    sed -i "s|/tmp/claude|${TERMUX_TMP}/claude|g" "$CLAUDE_JS"
    echo "Patched $(grep -o "${TERMUX_TMP}/claude" "$CLAUDE_JS" | wc -l) occurrences"
else
    echo "Already patched or file not found"
fi
SCRIPT
chmod +x $PREFIX/bin/claude-patch

Run claude-patch after every npm update -g @anthropic-ai/claude-code.

Why not proot?

proot intercepts every syscall via ptrace → 10-50% performance overhead. Direct patching has zero overhead.

Environment

  • Claude Code 2.1.42
  • Node.js v22.21.1 (LTS) — note: v25 is unsupported by Claude Code
  • Termux on Android (Galaxy Fold7, aarch64)

+1 for fixing this upstream with os.tmpdir(). It's a one-line fix that would help all Termux/Android/HPC users.

ferrumclaudepilgrim · 4 months ago

Adding an Android 16 specific data point — kernel 6.12, Samsung Galaxy S26 Ultra, One UI 8.5.

On Android 16 the proot-distro approach is completely broken — the kernel's updated security model breaks stdout file descriptor binding inside guest distributions. Processes launch but hang or produce no output. This is not a config issue.

Working solution on Android 16: native Termux install, skip proot-distro entirely, use proot bind mount only for /tmp at runtime.

export TMPDIR=$PREFIX/tmp
npm install -g @anthropic-ai/claude-code
pkg install proot -y
proot -b $PREFIX/tmp:/tmp claude

Node v25+ required — v24 hangs on ARM64 under Termux on this kernel.

Hoping the upstream fix in #31701 lands soon — that's the real solution for everyone.

Full Android 16 setup guide and troubleshooting reference: https://github.com/ferrumclaudepilgrim/claude-code-android

ferrumclaudepilgrim · 3 months ago

Correction to my earlier comment: I previously stated proot-distro was "completely broken" on Android 16. That was wrong.

The issue was a TCGETS2 ioctl bug in proot that was fixed in version 5.1.107-66 (October 2025). Current proot versions (5.1.107-70+) handle guest distros correctly on kernel 6.12. We verified this on three devices — Samsung Galaxy S26 Ultra, Google Pixel 10 Pro, and Samsung Galaxy S23+.

Two working approaches verified on real hardware:

Path A (native Termux): proot -b $PREFIX/tmp:/tmp claude — binds a writable tmp directory at the syscall level. This resolves the EACCES for basic operation AND subagent task directories (/tmp/claude/{hash}/tasks/). Verified working with subagents on Android 16.

Path B (proot-distro Ubuntu): Install Ubuntu via proot-distro, then use Anthropic's native installer (curl -fsSL https://claude.ai/install.sh | bash). No /tmp workaround needed — Ubuntu has native /tmp. No ripgrep symlink needed. No Node.js version management needed.

CLAUDE_CODE_TMPDIR is also documented as an alternative for users who prefer not to use proot.

Full guide with troubleshooting, verification test suite, and three-device compatibility data: https://github.com/ferrumclaudepilgrim/claude-code-android

wiilwaalka · 3 months ago
Correction to my earlier comment: I previously stated proot-distro was "completely broken" on Android 16. That was wrong. The issue was a TCGETS2 ioctl bug in proot that was fixed in version 5.1.107-66 (October 2025). Current proot versions (5.1.107-70+) handle guest distros correctly on kernel 6.12. We verified this on three devices — Samsung Galaxy S26 Ultra, Google Pixel 10 Pro, and Samsung Galaxy S23+. Two working approaches verified on real hardware: Path A (native Termux): proot -b $PREFIX/tmp:/tmp claude — binds a writable tmp directory at the syscall level. This resolves the EACCES for basic operation AND subagent task directories (/tmp/claude/{hash}/tasks/). Verified working with subagents on Android 16. Path B (proot-distro Ubuntu): Install Ubuntu via proot-distro, then use Anthropic's native installer (curl -fsSL https://claude.ai/install.sh | bash). No /tmp workaround needed — Ubuntu has native /tmp. No ripgrep symlink needed. No Node.js version management needed. CLAUDE_CODE_TMPDIR is also documented as an alternative for users who prefer not to use proot. Full guide with troubleshooting, verification test suite, and three-device compatibility data: https://github.com/ferrumclaudepilgrim/claude-code-android

Just tried it with npm install and it worked. No env variable or proot, maybe because i am using glibc buit nodejs and termux glibc userland is my default. I also permennatly unset LD_PRELOAD.

turboelectricltd-lab · 1 month ago

Simple workaround using termux-chroot:

For those who don't want to mess with proot -b bind mounts or environment variables, the simplest fix is:

pkg install proot -y
termux-chroot

This gives you a proper /tmp directory (and standard Linux filesystem layout) inside the chroot. Then just launch Claude Code as normal. The Bash tool works fine after this — no need for CLAUDE_CODE_TMPDIR or manual symlinks.

Tested on Android with kernel 4.19 (aarch64).

turboelectricltd-lab · 1 month ago

Follow-up: Confirmed that subagents (Task tool) also work correctly with the termux-chroot approach on Claude Code v2.1.112. The /tmp/claude/{hash}/tasks/ directory creation succeeds inside the chroot, so background tasks and subagent invocations run without EACCES errors.

Setup recap for anyone hitting this:

pkg install proot -y
termux-chroot
claude

Tested with Explore subagent and Bash tool — both functional.