[BUG] CLI completely hangs/deadlocks on Windows when CWD resolves to an existing User Profile directory (EEXIST in mkdirSync)
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?
When running the claude command in an interactive session on Windows, if the Current Working Directory (CWD) or the resolved project root directory happens to be exactly the user's home directory (e.g., C:\Users\<username>), the CLI stops responding completely without outputting any errors to the stdout/stderr. It hangs indefinitely and can only be forcefully terminated via Ctrl+C.
This often occurs seamlessly when claude is invoked inside a VS Code terminal where the workspace happens to be the user's home folder, or when Copilot Chat integrations share/lock the same
.claude.json
from the root path.
What Should Happen?
The CLI should successfully initialize, save its configuration, and show the interactive prompt/dashboard even when executed directly from the user's root directory.
Error Messages/Logs
Running with --debug-file reveals the exact point of failure:
[ERROR] Failed to save config with lock: Error: EEXIST: file already exists, mkdir 'C:\Users\17520'
[ERROR] Error: Error: EEXIST: file already exists, mkdir 'C:\Users\17520'
at mkdirSync (unknown)
...
The underlying issue stems from how the global/project configuration path is resolved and created:
When saving the configuration, the CLI extracts the directory name (C:\Users\17520) from the config path.
It then synchronously calls Node's fs.mkdirSync('C:\\Users\\17520') to ensure the directory exists.
Because C:\Users\17520 is a pre-existing root folder on Windows, and because the mkdirSync call is missing the { recursive: true } option, Node natively throws an EEXIST error.
This exception breaks the configuration lock/save promise chain. Because the CLI is also awaiting terminal UI interactions, the unhandled fatal error causes a deadlocked UI state instead of gracefully exiting, leaving the .lock file or internal locks hanging.
Steps to Reproduce
- Do NOT set the CLAUDE_CONFIG_DIR environment variable.
- Open a terminal and cd into your Windows User Profile directory (e.g., C:\Users\17520).
- Run claude (without the -p flag) so it triggers the interactive session and global config saving sequence.
- The terminal hangs and shows a blank state or a frozen permission dialog, becoming completely unresponsive to any input.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.68
Claude Code Version
2.1.69
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
Temporary Workaround: Setting the environment variable CLAUDE_CONFIG_DIR to a dedicated subfolder bypasses the bug, because the CLI correctly isolates its configuration into a path like C:\Users\17520\.claude_home and safely creates it over atomic temp files rather than trying to mkdirSync the Windows user dir itself:
[System.Environment]::SetEnvironmentVariable('CLAUDE_CONFIG_DIR', 'C:\Users\17520\.claude_home', 'User')
Suggested Fix: In your configuration persistence module (likely near the B38 / d38 functions found in the bundled JS), when executing fs.mkdirSync() to ensure the config parent directory exists, please pass the recursive flag:
// Change this:
fs.mkdirSync(dir);
// To this:
fs.mkdirSync(dir, { recursive: true });
Additionally, consider adding a try-catch block around the global config saver to prevent a silent hang if an I/O error occurs, so the lock can be released gracefully.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗