Repository navigation
[Bug] Windows: boot-time corpus scan exhausts process fd limit (EMFILE) and leaves a permanent empty .lock that blocks every later boot #9653
Replies: 1 comment
|
We hit the same failure independently on Some additional findings that may help narrow it down: 1. The "corpus scan" is the Our Also, 2. On Windows the empty lock appears even without the process dying. An exclusive create ( Output here: In 3. Why the table grows. A Fixes that would cover both sides, in line with your suggestions:
|
Uh oh!
There was an error while loading. Please reload this page.
[Bug] Windows: startup session-corpus scan exhausts the process fd limit (EMFILE), and the failure leaves a permanent empty
.lockthat blocks every later bootEnvironment
@deepseek-ai/dsh@0.2.1-alpha.2(npm global)dsh web(Node v24.18.0, Windows 11 x64)C:\Users\chkpk\.dshsessions\, ~8,268 files understorages\Summary
Two coupled problems, both reproduced deterministically today on one real machine. Neither is caused by a misbehaving third-party plugin: an empty-
DSH_HOMEcontrol boot with the same full web profile plugin tree starts and stays alive, while the real corpus boot dies ~4 s in.client-connection(required) writing the credentials file lock..credentials.yaml.lockthat nothing ever cleans up, and because it contains no holder PID it can never be taken over — every subsequent boot times out at the 2 s writer-lock deadline ("started but won't open"), until an operator deletes the file by hand.Reproduction
$DSH_HOMEto a few thousand session artifacts (mine: 2,129 log files undersessions\, 8,268 understorages\).dsh weband watch it fail within ~4 seconds.atomic-write: timed out waiting for the writer lock at C:\Users\chkpk\.dsh\.credentials.yaml.lock..credentials.yaml.lockmanually; the next boot reproduces step 2 (EMFILE) and recreates the empty lock.Control experiment (rules out the plugin tree):
Evidence
(a) Startup diagnostics (full diagnostic file kept, credentials redacted)
(b) The process fd limit on this machine (measured, not assumed)
(c) The lock file never receives its holder PID
dsh-atomic-write'swithFileLockcreates the lock withflag: "wx"and writes`${process.pid}\n`after creation. When the process dies from EMFILE between those two steps, the lock exists but is empty:takeOverExitedLock()cannot recover it: it reads the lock record to learn the holder PID, but an empty record has no PID to probe, so the takeover path never fires. The comment in the source says "A lock records its holder's PID" — in this failure mode it demonstrably does not.Kill-test (3 runs): every
Stop-Processof a boot leaveslen=0behind; every subsequent contender waits, times out afterDEFAULT_LOCK_WAIT_MS(2 s), and the cycle repeats.(d) Timeline match
The lock appears at t≈3.9–4.0 s and the EMFILE happens on the lock write — i.e. the corpus scan has already consumed the process's entire fd budget before any plugin needs a single fd for itself.
Suggested fixes
For (2), the lock itself (small, self-contained, fixes "boot fails forever after one bad boot"):
wx-created-but-empty lock older than a small grace window as abandoned and removable.wxsucceeds, remove the lock in afinally/catch before propagating.takeOverExitedLock()finds an empty record, treat it as a dead holder after the grace window instead of waiting forever.For (1), the fd exhaustion (larger, but the machine-independent part is clear):
Notes
All reactions