[BUG] file named nul created on windows
Environment
- Platform (select one):
- [ ] Anthropic API
- [x] Other: Claude CLI
- Claude CLI version: 1.0.62
- Operating System: Windows
- Terminal:
Bug Description
Claude CLI is creating an unwanted file named "nul" in the current directory during execution. This appears to be related to incorrect handling of null device redirection on Windows systems.
Steps to Reproduce
- Run Claude CLI commands on Windows with version 1.0.62
- Check the current working directory after command execution
- Observe that a file named "nul" has been created
Expected Behavior
No "nul" file should be created. Output redirection to null device should properly use Windows equivalent (>nul or 2>nul) without creating actual files.
Actual Behavior
A file named "nul" is created in the current directory, suggesting the CLI is attempting to redirect output using Unix-style /dev/null syntax which Windows interprets as a literal filename.
Additional Context
This issue is likely caused by cross-platform compatibility problems where Unix-style null device redirection (>/dev/null) is being used instead of Windows-appropriate syntax (>nul). The behavior appears intermittent ("sometimes created"), which may depend on specific command paths or error handling routines within the CLI.
Found 3 possible duplicate issues:
- https://github.com/anthropics/claude-code/issues/4206
- https://github.com/anthropics/claude-code/issues/3839
- https://github.com/anthropics/claude-code/issues/4535
If your issue is a duplicate, please close it and š the existing issue instead.
š¤ Generated with Claude Code
I'm still experiencing this on version 1.0.72
You can delete by prepending \?\ to the full path eg \?\c:\myfolder\nul On Sat, 9 Aug 2025 at 20:50, Robert McLaws @.***> wrote:
robertmclaws left a comment (anthropics/claude-code#4928) https://github.com/anthropics/claude-code/issues/4928#issuecomment-3172030845
Also, I can't delete the file in Windows which is really annoying.
ā Reply to this email directly, view it on GitHub https://github.com/anthropics/claude-code/issues/4928#issuecomment-3172030845, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABZZRQY4I4FERLU65YXRAUD3MY7GNAVCNFSM6AAAAACC445NFGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTCNZSGAZTAOBUGU . You are receiving this because you authored the thread.Message ID: @.***>
The way I've been deleting them is to add them in 7zip and have it delete the file after compressing. Really dumb, but the nul file borks my Git commits otherwise.
Confirmed same issue
I'm having the same issue.
Confirmed issue in the version: 1.0.90
Just happened, claude code v1.0.95
Super annoying. Please fix.
Super annoying. Please fix.
Hi, I found that in my case, this was happening because I had a Unix configuration in the additionalDirectories parameter of the .claude\settings.local.json file.
Updating it to the correct path prevented it from happening again.
Aha, is this maybe related to when you work on the same project using both Windows and Linux PCs? I've noticed the Windows version getting better at "knowing" whether it is running in Windows or Linux...
Aha, is this maybe related to when you work on the same project using both Windows and Linux PCs? I've noticed the Windows version getting better at "knowing" whether it is running in Windows or Linux...
Maybe when enabled this config (in webstorm plugin config)
I'm not running Windows in Linux or vice versa, but I've been using GitBash for Windows and this file is being created from time to time.
Funny thing is, I could not delete the file without elevated permission, but Claude code once found it and easily removed it on its own.
Ask claude code to remove the "nul" file that it created. This worked for me.
Ask claude code to remove the "nul" file that it created. This worked for me.
genius! - works for me
I encountered the same issue with additional details that might help narrow down the root cause:
Trigger: Claude Code 2.0.8 was analyzing project file structure
File content (864 bytes):
$ cat nul
File "<string>", line 1
import os; from pathlib import Path; [print(' ' * (len(p.parts) - len(Path.cwd().parts)) + ('āāāāāā ' if i < len(list(Path(root).parent.glob('*'))) - 1 else 'āāāāāā ') + p.name) for root, dirs, files in os.walk('.') for i, p in enumerate([Path(root)] + [Path(root) / d for d in sorted(dirs)] + [Path(root) / f for f in sorted(files)]) if p.is_file() or (p.is_dir() and p \!= Path(root))]
^
SyntaxError: unexpected character after line continuation character
This suggests Claude Code tried to generate a file tree visualization using a Python one-liner, which had a syntax error. The stderr was redirected to nul but created a physical file instead.
Specific code path: File tree analysis/project structure inspection
This might help pinpoint which part of the CLI codebase is causing the issue.
Additional note on file access:
The nul file can be read and deleted normally using Git Bash:
# Read content
cat nul
# Delete
rm nul
However, Windows native tools fail:
- PowerShell: "Cannot find path" (even with UNC path
\\?\...) - File Explorer: Shows file but properties are empty
Get-FileHash: "Path does not exist"
This confirms the issue is at the Win32 API semantic layer - Git Bash directly accesses NTFS and treats nul as a regular filename, bypassing Windows reserved name checks.
Workaround for users: If you encounter this issue, use Git Bash to remove the file:
rm nul
This is ruining my commits on a regular basis. I use plastic for game dev and nul causes chaos on the filesystem and can absolutely corrupt the source control history.
This suddenly started happening to me today when I was just working on a regular React app. I made some prompts to change the JSX and there was the nul file with no apparent reason. Some of the operations I did was removing a Unicode character in a TypeScript file, removing a redundant import, and just working on regular JSX. Now it always creates the nul file when I open VS Code, doesn't even require input from me. And I'm purely working on a Windows machine.
Can confirm. Claude Code created a literal nul file during git merge operations on Windows with Git Bash. Appears to be using 2>nul (Windows CMD syntax) instead of 2>/dev/null in a Unix shell context.
Can confirm with version 2.0.22
Aurrgggghhhhh...
Very annoying bug to contend with during dev, while trying to save tokens -- I don't want to waste tokens asking Claude to clean up its mess.
same here. Windows11 running in a powershell. The Claude CLI starts having errors once the NUL file is created. I'm trying to fix that wit a skill (remove-nul). But Claude has a hard time remembering its skills.
Comfirmed issu in multiple projects. Same issue here. Windows 11 Powershell.
Got it - null on each project folder. can't copy, hard to delete. created everytime when run claude code.
I ended up writing a Python script to clean up all the folders:
#!/usr/bin/env python3
"""
NUL Files Removal Script for Windows
This script finds and removes files named 'nul' which are problematic on Windows.
'nul' is a reserved device name in Windows (like CON, PRN, AUX) and cannot be
manipulated using standard file operations.
The script uses Windows UNC path syntax (\\?\) to bypass the reserved name
restriction and safely remove these files.
Usage:
python remove_nul_files.py [directory]
If no directory is specified, defaults to the script's directory
"""
import os
import sys
from pathlib import Path
def find_nul_files(root_path, verbose=True):
"""
Recursively search for files named 'nul'.
Args:
root_path: The root directory to search from
verbose: Whether to print search progress (default: True)
Returns:
List of paths to 'nul' files found
"""
nul_files = []
if verbose:
print(f"Searching for 'nul' files in: {root_path}")
print("-" * 60)
try:
# Walk through all directories
for dirpath, _, filenames in os.walk(root_path):
# Check for files named 'nul'
if 'nul' in filenames:
nul_file_path = os.path.join(dirpath, 'nul')
nul_files.append(nul_file_path)
except Exception as e:
print(f"Warning: Error during search: {e}")
return nul_files
def remove_nul_files(nul_files):
"""
Remove the specified nul files.
Uses Windows UNC path syntax (\\?\) to handle reserved names like 'nul'.
Args:
nul_files: List of file paths to remove
Returns:
Tuple of (removed_count, error_count)
Notes:
- Uses UNC path prefix (\\?\) to bypass Windows reserved name restrictions
- Handles symlinks and junctions that may point to the same physical file
- Returns count of removed files and errors encountered
"""
removed_files = []
errors = []
if not nul_files:
return 0, 0
print("\nRemoving files...")
print("-" * 60)
for nul_file_path in nul_files:
try:
# Use pathlib to get absolute path WITHOUT resolving symlinks/junctions
path_obj = Path(nul_file_path)
# Get absolute path without resolving symlinks (use absolute() instead of resolve())
abs_path = str(path_obj.absolute())
# Normalize backslashes
abs_path = abs_path.replace('/', '\\')
# Add \\?\ prefix for Windows long path / reserved name support
# This bypasses the Windows reserved name check
unc_path = f"\\\\?\\{abs_path}"
os.remove(unc_path)
removed_files.append(nul_file_path)
print(f"ā Removed: {nul_file_path}")
except FileNotFoundError:
# File might have been already deleted (symlink/junction pointing to same file)
print(f"ā Already removed (symlink/junction): {nul_file_path}")
removed_files.append(nul_file_path)
except Exception as e:
errors.append((nul_file_path, str(e)))
print(f"ā Failed to remove: {nul_file_path}")
print(f" Error: {e}")
# Summary
print("-" * 60)
print(f"\nSummary:")
print(f" Files removed: {len(removed_files)}")
print(f" Errors: {len(errors)}")
if removed_files:
print("\nRemoved files:")
for file in removed_files:
print(f" - {file}")
if errors:
print("\nErrors:")
for file, error in errors:
print(f" - {file}: {error}")
return len(removed_files), len(errors)
def main():
"""Main execution function."""
# Default to the directory where the script is located
script_dir = os.path.dirname(os.path.abspath(__file__))
target_dir = script_dir
# Allow override via command line argument
if len(sys.argv) > 1:
target_dir = sys.argv[1]
# Validate directory exists
if not os.path.exists(target_dir):
print(f"Error: Directory '{target_dir}' does not exist!")
input("\nPress ENTER to close...")
sys.exit(1)
if not os.path.isdir(target_dir):
print(f"Error: '{target_dir}' is not a directory!")
input("\nPress ENTER to close...")
sys.exit(1)
print("=" * 60)
print("NUL Files Removal Script")
print("=" * 60)
print(f"Target directory: {target_dir}")
print("=" * 60)
try:
# First, find all nul files
nul_files = find_nul_files(target_dir)
if not nul_files:
print("\nā No 'nul' files found!")
input("\nPress ENTER to close...")
sys.exit(0)
# Display found files
print(f"\nFound {len(nul_files)} 'nul' file(s):")
print("-" * 60)
for file in nul_files:
print(f" - {file}")
print("-" * 60)
# Ask for confirmation
input(f"\nPress ENTER to delete these {len(nul_files)} file(s)... (or Ctrl+C to cancel)")
print("")
# Remove the files
removed, errors = remove_nul_files(nul_files)
# Final verification step
print("\n" + "=" * 60)
print("FINAL VERIFICATION")
print("=" * 60)
remaining_nul_files = find_nul_files(target_dir, verbose=False)
if not remaining_nul_files:
print("\nā SUCCESS: All 'nul' files have been removed!")
print(f" {removed} file(s) successfully deleted")
else:
print(f"\nā WARNING: {len(remaining_nul_files)} 'nul' file(s) remaining:")
for file in remaining_nul_files:
print(f" - {file}")
print("\n" + "=" * 60)
input("\nPress ENTER to close...")
if errors > 0 or remaining_nul_files:
sys.exit(1)
else:
sys.exit(0)
except KeyboardInterrupt:
print("\n\nOperation cancelled by user.")
input("\nPress ENTER to close...")
sys.exit(130)
except Exception as e:
print(f"\nUnexpected error: {e}")
print(f"Error type: {type(e).__name__}")
input("\nPress ENTER to close...")
sys.exit(1)
if __name__ == "__main__":
main()
When will this be fixed? No matter how I write in claude md file to check the OS and to use correct cmd shell, it still makes them all the time.
This helps to clean up but still it can happen it makes a ton of them not just one.
# PowerShell script to delete all "nul" files created by mistake
# This uses .NET File.Delete with \\?\ prefix to bypass Windows reserved name handling
Write-Host "Finding all 'nul' files..." -ForegroundColor Yellow
$nulFiles = Get-ChildItem -Path "d:\yourfolderhere" -Filter "nul" -Recurse -Force -ErrorAction SilentlyContinue
$totalFiles = $nulFiles.Count
Write-Host "Found $totalFiles 'nul' files to delete" -ForegroundColor Cyan
$successCount = 0
$failCount = 0
foreach ($file in $nulFiles) {
try {
$fullPath = $file.FullName
$extendedPath = "\\?\$fullPath"
[System.IO.File]::Delete($extendedPath)
$successCount++
if ($successCount % 100 -eq 0) {
Write-Host "Deleted $successCount / $totalFiles files..." -ForegroundColor Green
}
} catch {
Write-Host "Failed to delete: $($file.FullName) - $_" -ForegroundColor Red
$failCount++
}
}
Write-Host ""
Write-Host "=== Deletion Complete ===" -ForegroundColor Green
Write-Host "Successfully deleted: $successCount files" -ForegroundColor Green
Write-Host "Failed: $failCount files" -ForegroundColor Red
Write-Host ""
Write-Host "Press any key to exit..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Yes it sucks and maybe I'm naive, but I just put it in the gitignore and have had no issues.
Yes it sucks and maybe I'm naive, but I just put it in the gitignore and have had no issues.
Interesting, I am worried if I do that maybe something in Windows might break?
Codex asked me to install WSL so maybe we could ask Claude to use that too, if it likes more to talk in Linux talk.
Yes it sucks and maybe I'm naive, but I just put it in the gitignore and have had no issues.
Interesting, I am worried if I do that maybe something in Windows might break?
Codex asked me to install WSL so maybe we could ask Claude to use that too, if it likes more to talk in Linux talk.
Haven't had an issue at all.
When I first started using windsurf I was using WSL and it sucked. I finally got things sorted and I'm working straight with Windows.
I've been running 3 Claude Code terminal sessions on three different monitors non-stop for about 10 days.
I hit my limit about 30 minutes before the reset yesterday and today I hit my limit just over an hour before the reset. (I'm getting more efficient in my prompting)
I'm on the $100 a month plan and have been just rocking it.
Yeah, adding it to the gitignore was the simplest solution and have had no problems.