[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
Preflight Checklist
- [x] I have searched existing issues. Closest: #78013 (export the scratchpad path as
CLAUDE_SCRATCHPAD, closed stale) and #91676 (macOS sandbox: empty$TMPDIRfed intorm -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:
- On Windows, set
TMPDIRin the Bash tool's child environment toos.tmpdir()(the same valueTMP/TEMPcarry), so$TMPDIR/namelands in%LOCALAPPDATA%\Templike it does on macOS/Linux. - 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
- Windows, Git for Windows installed, Claude Code with the Bash tool.
- Ask: "write a three-line note to a temp file with a heredoc and cat it back".
- Observe the first attempt use
$TMPDIR/...and fail withPermission deniedon 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=[].