[BUG] Stop hook fails with EEXIST when plugin data directory already exists
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Stop hooks from plugins fail with EEXIST: file already exists, mkdir when the plugin data directory already exists. The error occurs on every stop hook invocation after the first run.
This is the same root cause as several other EEXIST bugs (#33118, #36919, #37306) — all are missing { recursive: true } on mkdir calls. Closing #39108 as a duplicate in favor of this report.
What Should Happen?
The plugin data directory creation should use recursive/idempotent mkdir (fs.mkdir with { recursive: true }) so it doesn't fail if the directory already exists.
Error Messages/Logs
Ran 1 stop hook (ctrl+o to expand)
⎿ Stop hook error: Failed to run: EEXIST: file already exists, mkdir
'C:\Users\PC\.claude\plugins\data\qingyi-qingyi'
The directory already exists from a previous session:
$ ls ~/.claude/plugins/data/
qingyi-qingyi/
superpowers-claude-plugins-official/
Steps to Reproduce
- Install a plugin that uses a stop hook
- Run a Claude Code session — stop hook works on first run (creates data dir)
- Run another session — stop hook fails because directory already exists
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.79
Claude Code Version
2.1.85
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
IntelliJ IDEA terminal
Additional Information
The hook itself never executes — the error is in the plugin infrastructure before the hook runs. The fix should be a one-line change: use fs.mkdir(path, { recursive: true }) instead of fs.mkdir(path) when creating the plugin data directory.
5 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Workaround: make Stop hooks resilient to filesystem errors:
Confirming on v2.1.100 (Windows 11 Pro, Git Bash, Node v22).
Plugin:
azure@azure-skills(scope: local, v1.0.14 -- latest)Hook type: PostToolUse:Bash -- not limited to Stop hooks
Actual error from Claude Code:
The data directory exists but is empty. The hook script never executes -- the error is thrown by the plugin runtime before the shell process spawns, so plugin-side error handling (
set +e, early exit) cannot mitigate it.Standard Node.js behavior confirms the pattern:
Two additions to the original report:
mkdircall appears to be in a shared plugin dispatch path.Cross-linking from #55427 (auto-closed as duplicate) — same root cause but with fully blocking impact worth surfacing here.
In addition to the plugin data-directory mkdir, the same
EEXISTpattern affects the Bash tool's per-session env directory (~/.claude/session-env/<uuid>). The user-facing consequence is far more severe than the plugin case:EEXISTtriggers (more likely under concurrent Bash invocations on Windows), every subsequent Bash call in the same session fails. The tool layer enters a permanent broken state. Closing/restarting Claude Code is the only recovery; in some cases the on-disksession-envfolder must be deleted manually before the next start.Repro signal
Reproduced reliably on Claude Code 2.1.119 (Windows 11, Git Bash) by issuing 2–3 long-running Bash invocations concurrently (e.g.
npm test,npm run typecheck,npm run lintin parallel). The first errors out, all subsequent Bash calls in the session emitEEXISTimmediately without executing.This confirms the regression spans 2.1.85 → 2.1.119.
Workflow impact
Blocks all shell-based work in the affected session:
npm,gh, builds, tests — everything. The only way to recover is restart + (sometimes) manual folder removal.Suggested fix (unchanged)
fs.mkdir(path, { recursive: true })— or a try/catch that swallowsEEXIST— at both call sites:~/.claude/plugins/data/<plugin>-<marketplace>).~/.claude/session-env/<uuid>).Happy to provide additional logs / a tighter repro if useful.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.