EBADF: bad file descriptor error on history.jsonl.lock (Windows race condition)
Resolved 💬 6 comments Opened Dec 29, 2025 by pgb123au Closed Feb 28, 2026
Description
Intermittent EBADF: bad file descriptor error occurs on Windows when Claude Code attempts to resolve the path of the history lock file.
Error Message
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
Error: EBADF: bad file descriptor, realpath 'C:\Users\peter\.claude\history.jsonl.lock'
at async realpath (node:internal/fs/promises:1164:10)
at async mi1._addToNodeFs (file:///C:/Users/peter/AppData/Roaming/npm/node_modules/@anthropic-ai/claude-code/cli.js:448:10239)
Environment
- OS: Windows 11
- Claude Code Version: 2.0.76
- Node.js: (bundled with Claude Code)
Root Cause Analysis
This appears to be a race condition where:
- A lock file (
history.jsonl.lock) is created realpath()is called asynchronously to resolve its path- The lock file is deleted before
realpath()completes - Node.js returns EBADF because the file descriptor is no longer valid
What Was Tried (Did NOT Fix)
- Adding Windows Defender exclusion for
~/.claudefolder (exclusion was already present) - Checking file permissions (all correct)
- Clearing stale
.lockfiles
Workaround That Helped
Trimming history.jsonl to reduce file size/lock contention:
tail -100 ~/.claude/history.jsonl > ~/.claude/history.jsonl.tmp && mv ~/.claude/history.jsonl.tmp ~/.claude/history.jsonl
Suggested Fix
The async realpath() call should be wrapped in a try-catch, or the lock file existence should be verified before calling realpath(). Alternatively, use synchronous realpathSync() for lock file operations to avoid the race condition.
Reproduction
Occurs intermittently during normal Claude Code usage on Windows. More frequent when history.jsonl is large (800+ lines).
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗