Repository navigation
fix: respect $TMPDIR and os.tmpdir() instead of hardcoding /tmp/claude - #31701
rios0rios0 wants to merge 1 commit into
Conversation
Add patch script and documentation to fix hardcoded /tmp/claude paths that break Claude Code on Termux/Android and other restricted environments where /tmp is not writable. The patch script (scripts/patch-tmp-paths.sh) replaces 6 hardcoded /tmp references in cli.js with dynamic resolution using the same priority chain as Node.js os.tmpdir(): $TMPDIR > $TEMP > $TMP > /tmp. Affected operations now fixed: - Main temp directory resolution (session data, scratchpads) - Sandbox TMPDIR environment variable - Shell command sandbox temp directory - Screenshot capture paths - MCP browser bridge socket directory - Sandbox path allowlist Also includes: - scripts/termux-setup.sh for guided Termux setup - README.md section documenting Termux/Android usage Fixes #15628, fixes #15637, fixes #16955, fixes #17366, fixes #18342 Relates to #23634 https://claude.ai/code/session_013FEYM2EYhLJeVEZZbmFTqD
There was a problem hiding this comment.
Pull request overview
Adds Termux/Android support guidance and helper scripts to avoid failures caused by hardcoded /tmp/claude paths in restricted environments (e.g., Termux where /tmp isn’t writable).
Changes:
- Add
scripts/patch-tmp-paths.shto patch bundledcli.jshardcoded/tmpreferences to dynamic temp-dir resolution. - Add
scripts/termux-setup.shto configure a Termux-compatible temp directory and optionally run the patch. - Document Termux/Android setup steps in
README.md.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| scripts/termux-setup.sh | Guided Termux setup: chooses writable tmp base, updates shell profile, optionally runs patcher. |
| scripts/patch-tmp-paths.sh | Patches specific string patterns in cli.js to use env-driven temp directory selection. |
| README.md | Adds Termux/Android usage instructions and patch command guidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| **Quick setup:** | ||
| ```bash | ||
| bash scripts/termux-setup.sh |
There was a problem hiding this comment.
The “Quick setup” command assumes the user has a checkout of this repository (and that scripts/termux-setup.sh exists locally). Given the earlier install instructions recommend curl/homebrew/winget, it would help to clarify that Termux users should clone the repo (or provide a curl/wget invocation to the raw script).
| bash scripts/termux-setup.sh | |
| # Option 1: run directly from GitHub (no local checkout required) | |
| curl -fsSL https://raw.githubusercontent.com/anthropics/claude-code/HEAD/scripts/termux-setup.sh | bash | |
| # Option 2: from a cloned repo | |
| # git clone https://github.com/anthropics/claude-code.git | |
| # cd claude-code | |
| # bash scripts/termux-setup.sh |
|
|
||
| **Manual setup** — add this to your shell profile (`~/.bashrc` or `~/.zshrc`): | ||
| ```bash | ||
| export CLAUDE_CODE_TMPDIR="$TMPDIR" |
There was a problem hiding this comment.
Manual setup exports CLAUDE_CODE_TMPDIR="$TMPDIR", but on some shells/environments TMPDIR may be unset. Consider documenting a safe fallback (e.g., using ${TMPDIR:-/tmp} or deriving it from Node’s os.tmpdir()), so users don’t accidentally set the variable to an empty string.
| export CLAUDE_CODE_TMPDIR="$TMPDIR" | |
| export CLAUDE_CODE_TMPDIR="${TMPDIR:-/tmp}" |
|
|
||
| **If you still encounter `/tmp/claude` permission errors**, run the patch script to fix hardcoded paths in the bundled `cli.js`: | ||
| ```bash | ||
| bash scripts/patch-tmp-paths.sh "$(command -v claude | xargs dirname)/../lib/node_modules/@anthropic-ai/claude-code/cli.js" |
There was a problem hiding this comment.
The patch command uses command -v claude | xargs dirname; using dirname "$(command -v claude)" is simpler and avoids xargs quirks (e.g., unexpected splitting) in case the path contains whitespace.
| bash scripts/patch-tmp-paths.sh "$(command -v claude | xargs dirname)/../lib/node_modules/@anthropic-ai/claude-code/cli.js" | |
| bash scripts/patch-tmp-paths.sh "$(dirname "$(command -v claude)")/../lib/node_modules/@anthropic-ai/claude-code/cli.js" |
| echo "Warning: This does not appear to be a Termux environment." | ||
| echo "This script is designed for Termux on Android." | ||
| echo "" | ||
| read -rp "Continue anyway? (y/N) " answer |
There was a problem hiding this comment.
Because the script runs with set -e, the read -rp prompt will cause the script to exit with an error in non-interactive contexts (e.g., when run via a wrapper with stdin closed). Consider only prompting when stdin is a TTY (e.g., [[ -t 0 ]]), or make the read non-fatal and default to "no" when input isn’t available.
| read -rp "Continue anyway? (y/N) " answer | |
| if [[ -t 0 ]]; then | |
| read -rp "Continue anyway? (y/N) " answer | |
| else | |
| answer="n" | |
| fi |
| if [ -f "$PROFILE" ] && grep -qF "CLAUDE_CODE_TMPDIR" "$PROFILE"; then | ||
| echo " [SKIP] CLAUDE_CODE_TMPDIR already set in $PROFILE" | ||
| else | ||
| echo "" >> "$PROFILE" | ||
| echo "# Claude Code — use Termux-compatible temp directory" >> "$PROFILE" | ||
| echo "$EXPORT_LINE" >> "$PROFILE" | ||
| echo " [OK] Added to $PROFILE:" |
There was a problem hiding this comment.
The profile check grep -qF "CLAUDE_CODE_TMPDIR" will skip updating even if the variable is commented out or set to a different value. To avoid leaving users misconfigured, consider checking for the exact export line you intend to add, or updating/replacing an existing export CLAUDE_CODE_TMPDIR=... entry.
| if [ -f "$PROFILE" ] && grep -qF "CLAUDE_CODE_TMPDIR" "$PROFILE"; then | |
| echo " [SKIP] CLAUDE_CODE_TMPDIR already set in $PROFILE" | |
| else | |
| echo "" >> "$PROFILE" | |
| echo "# Claude Code — use Termux-compatible temp directory" >> "$PROFILE" | |
| echo "$EXPORT_LINE" >> "$PROFILE" | |
| echo " [OK] Added to $PROFILE:" | |
| if [ -f "$PROFILE" ]; then | |
| if grep -qE '^[[:space:]]*export[[:space:]]+CLAUDE_CODE_TMPDIR=' "$PROFILE"; then | |
| # Replace existing export with the new value | |
| sed -i.bak -E 's|^[[:space:]]*export[[:space:]]+CLAUDE_CODE_TMPDIR=.*$|'"$EXPORT_LINE"'|' "$PROFILE" | |
| echo " [OK] Updated CLAUDE_CODE_TMPDIR in $PROFILE" | |
| elif grep -q "CLAUDE_CODE_TMPDIR" "$PROFILE"; then | |
| # Variable mentioned only in comments or non-export lines; append a correct export | |
| echo "" >> "$PROFILE" | |
| echo "# Claude Code — use Termux-compatible temp directory" >> "$PROFILE" | |
| echo "$EXPORT_LINE" >> "$PROFILE" | |
| echo " [OK] Added CLAUDE_CODE_TMPDIR export to $PROFILE (existing commented reference found)" | |
| echo " $EXPORT_LINE" | |
| else | |
| echo "" >> "$PROFILE" | |
| echo "# Claude Code — use Termux-compatible temp directory" >> "$PROFILE" | |
| echo "$EXPORT_LINE" >> "$PROFILE" | |
| echo " [OK] Added CLAUDE_CODE_TMPDIR export to $PROFILE:" | |
| echo " $EXPORT_LINE" | |
| fi | |
| else | |
| echo "" >> "$PROFILE" | |
| echo "# Claude Code — use Termux-compatible temp directory" >> "$PROFILE" | |
| echo "$EXPORT_LINE" >> "$PROFILE" | |
| echo " [OK] Created $PROFILE and added CLAUDE_CODE_TMPDIR export:" |
| ( | ||
| "MCP browser bridge dir (jd6)", | ||
| "return`/tmp/claude-mcp-browser-bridge-", | ||
| 'return`${process.env.TMPDIR||process.env.TEMP||process.env.TMP||"/tmp"}/claude-mcp-browser-bridge-', | ||
| ), |
There was a problem hiding this comment.
The header comment says the patched resolution prioritizes $CLAUDE_CODE_TMPDIR, but this patch uses only $TMPDIR/$TEMP/$TMP (ignoring process.env.CLAUDE_CODE_TMPDIR). If a user sets only CLAUDE_CODE_TMPDIR (as suggested elsewhere), the MCP browser bridge path will still fall back to /tmp. Consider including process.env.CLAUDE_CODE_TMPDIR|| ahead of the TMPDIR chain for these replacements.
| ( | ||
| "Sandbox path allowlist ($x6)", | ||
| '"/tmp/claude","/private/tmp/claude"', | ||
| f'"/tmp/claude","/private/tmp/claude",{TMPDIR}+"/claude"', |
There was a problem hiding this comment.
The sandbox allowlist patch adds (process.env.TMPDIR||process.env.TEMP||process.env.TMP||"/tmp")+"/claude", but does not include process.env.CLAUDE_CODE_TMPDIR. If the runtime override is set via CLAUDE_CODE_TMPDIR, the allowlist still won’t include that path. Consider using process.env.CLAUDE_CODE_TMPDIR||… here to match the documented priority chain.
| f'"/tmp/claude","/private/tmp/claude",{TMPDIR}+"/claude"', | |
| f'"/tmp/claude","/private/tmp/claude",(process.env.CLAUDE_CODE_TMPDIR||{TMPDIR})+"/claude"', |
| if content != original: | ||
| with open(cli_js_path, "w", encoding="utf-8") as f: | ||
| f.write(content) |
There was a problem hiding this comment.
The patch writes changes back to cli.js in-place without creating a backup or using an atomic write. If the process is interrupted (or the filesystem fills up), this can leave users with a corrupted cli.js. Consider writing to a temporary file and renaming, and/or creating a .bak copy before modifying.
Workaround: Claude Code on Termux/AndroidI've been running Claude Code on Termux (Android) and hit two blocking incompatibilities. I put together a workaround that patches both issues automatically on every shell startup, surviving The Problems1. Hardcoded Claude Code's bundled The 2. Missing vendored Claude Code ships platform-specific The SolutionThree files, two of which are standard shell configuration ( File 1 — Add this block before any PATH setup: # On Termux/Android, /tmp is owned by shell:shell (0771) and not writable by app processes.
# TMPDIR must be set before any tool (Node.js, Claude Code, etc.) calls os.tmpdir(),
# which defaults to /tmp on Linux when TMPDIR is unset.
if [[ -n "$PREFIX" && -d "$PREFIX/tmp" ]]; then
export TMPDIR="${TMPDIR:-$PREFIX/tmp}"
fiThis ensures File 2 — # Workaround: Claude Code hardcodes /tmp paths which are not writable on Termux.
# This script patches cli.js to use process.env.TMPDIR and symlinks system ripgrep.
# See: https://github.com/anthropics/claude-code/issues/15637
source "$HOME/.scripts/android-patch-claude-code-tmpdir.sh"File 3 — This is the core of the workaround. It:
The full script is available in my dotfiles repository: The other two files (note: my dotfiles use chezmoi, so
Prerequisitespkg install ripgrep # required for the Grep/Glob tools fixAfter Updating Claude CodeWhen you run |
Add patch script and documentation to fix hardcoded /tmp/claude paths that break Claude Code on Termux/Android and other restricted environments where /tmp is not writable.
The patch script (scripts/patch-tmp-paths.sh) replaces 6 hardcoded /tmp references in cli.js with dynamic resolution using the same priority chain as Node.js os.tmpdir(): $TMPDIR > $TEMP > $TMP > /tmp.
Affected operations now fixed:
Also includes:
Related Issues
Fixes #15628, fixes #15637, fixes #16955, fixes #17366, fixes #18342, relates to #23634
These issues collectively have 20+ upvotes and 8+ duplicates, affecting all Termux/Android users since December 2025. All were auto-closed by the stale bot without being fixed.