[BUG] Windows: Bash tool has no $TMPDIR (Git Bash sets only TMP/TEMP) and no scratchpad env var, so $TMPDIR/file expands to a root path and fails

Status Open
Reported on v2.1.278
Maintainer reply None cached
Activity 0 comments · opened Sep 21, 2026

Preflight Checklist

  • [x] I have searched existing issues. Closest: #78013 (export the scratchpad path as CLAUDE_SCRATCHPAD, closed stale) and #91676 (macOS sandbox: empty $TMPDIR fed into rm -rf). Neither covers the Windows / Git Bash case below.
  • [x] Single bug report.
  • [x] Claude Code 2.1.278 (native) and 2.1.275 (Claude Desktop bundled), Windows 11, Bash tool backed by Git for Windows bash.

What's Wrong?

On Windows, the Bash tool's shell has no TMPDIR. Git for Windows (MSYS2) sets TMP and TEMP but not TMPDIR, and Claude Code does not add one. The model's general habit for a temp file is $TMPDIR/name, so a command like

cat > "$TMPDIR/issue-body.md" <<'EOF'
...
EOF

expands to /issue-body.md and fails:

/usr/bin/bash: line 45: /issue-body.md: Permission denied

The model then notices, rewrites the command against the scratchpad path from the system prompt (or /tmp, which maps to a different place than os.tmpdir()), and continues. Each occurrence costs one round trip and a re-run of the command; when the failed command was a gh issue view … > "$TMPDIR/x" or similar, the side effects before the redirection have already run once.

The scratchpad directory named in the # Scratchpad Directory system-prompt block is not reachable from the shell either: no environment variable carries it (CLAUDE_SCRATCHPAD is unset; CLAUDE_CODE_SESSION_ID is present). So the two places the model reaches for a temp path are both empty from inside Bash.

Frequency

I scanned my own transcripts (~/.claude/projects/**/*.jsonl, 2,866 files, June–September 2026): 67 sessions used $TMPDIR in a Bash command; 21 of them hit …: Permission denied on a root-level path from the empty expansion (July 4, August 6, September 11 sessions), 16 succeeded for other reasons (WSL-side shells, ${TMPDIR:-/tmp}), 5 errored otherwise. Only 2 sessions said "TMPDIR is empty" in prose; the rest silently retried, which is why this is easy to miss.

Expected

Either or both:

  1. On Windows, set TMPDIR in the Bash tool's child environment to os.tmpdir() (the same value TMP/TEMP carry), so $TMPDIR/name lands in %LOCALAPPDATA%\Temp like it does on macOS/Linux.
  2. Export the session scratchpad path to tool processes (the ask in #78013), so the directory the system prompt tells the model to use is the one the shell can name.

Workaround

~/.claude/settings.json:

{ "env": { "TMPDIR": "C:\Users\<user>\AppData\Local\Temp" } }

Steps to Reproduce

  1. Windows, Git for Windows installed, Claude Code with the Bash tool.
  2. Ask: "write a three-line note to a temp file with a heredoc and cat it back".
  3. Observe the first attempt use $TMPDIR/... and fail with Permission denied on a root-level path; observe the retry.

Or directly: echo "TMPDIR=[$TMPDIR] TMP=[$TMP] CLAUDE_SCRATCHPAD=[$CLAUDE_SCRATCHPAD]" in the Bash tool prints TMPDIR=[] TMP=[C:\Users\…\AppData\Local\Temp] CLAUDE_SCRATCHPAD=[].

View original on GitHub ↗