[Bug] OutputCollector.spillAll writeSync is uncaught: EDQUOT/ENOSPC kills the whole dsh web host #5175
NOirBRight
started this conversation in
General
Replies: 1 comment
|
源码侧全部核实(本地 0.1.2-alpha.1 = cd5ef81,与你引用的 staging 版本同源)。你的故障链每一步都能在源码里对上,且这个 bug 属于一个已知家族——你命中的正是家族此前追踪过的同一函数同一机制,EDQUOT 是第二个触发类。 核实结果(逐条,packages/subprocess/subprocess-local/src/spawn.ts):
家族归并(关键增量):#3190(Windows 11, rc.7)报告的正是同一个函数同一个机制——外部 %TEMP% 清理删掉运行中的 spill 目录 →
一句话:修复 = 把 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Environment
dsh-v0.1.2-alpha.1-cd5ef8148158(official alpha.1 staging build)tmp.mounthasx-systemd.graceful-option=usrquota)dsh webunder a user systemd unit withRestart=always中文摘要
bash 输出超过内存上限(默认 64KB)后,
OutputCollector.spillAll用同步writeSync往os.tmpdir()(通常是/tmp)写溢出文件。Linux + systemd ≥258 给/tmptmpfs 加了约 80% 的每用户配额。配额一满,df仍显示有空闲,但writeSync抛EDQUOT(Node 报code: 'UNKNOWN', errno: -122)。这条写在stream.on('data')回调里、没有 try/catch,整个dsh web进程退出。systemd 拉起后,所有进行中的 turn 变成turn/end { kind: interrupted },看起来像模型自己停了。同文件的
closeSync/unlinkSync已经接住了;openSync/writeSync没有。这不是用户点停止,也不是 model picker。Symptom
The host Node process aborts. systemd restarts it a few seconds later. Every in-flight turn is recorded as:
{"type":"turn/end","data":{"reason":{"kind":"interrupted"}}}Not a user abort, not a model-picker side effect. The UI just looks like the agent stopped mid-run.
Repro class
/tmpis tmpfs with per-user quota (default ~80% of the tmpfs, itself ~50% of RAM)./tmp(test stores, Playwright artifacts, leftoverdsh-subprocess-*spill dirs).df /tmpcan still show gigabytes free — that free space is not available to the quota-exhausted user.maxOutputBytes(default 64KB).OutputCollectoropens a spill file andwriteSyncs.Once (2) is true, (3) kills the whole host. After a restart the next overflowing bash does it again.
A 1-byte write to
/tmpas that user fails with[Errno 122] Disk quota exceededwhiledf -h /tmpstill reports free space.Production evidence (2026-08-31)
Five identical crashes on one
dsh webhost, 11:04 / 11:12 / 11:14 / 11:15 / 11:29 local time. Representative journal:Linux
122isEDQUOT. Node/libuv does not map it, so the JS error isUNKNOWN.Spill files created at crash time were 0 bytes:
openSync(..., 'wx')succeeded, the firstwriteSyncfailed. The first crash in that series had been up 11h44m (9.5G memory peak); the later four died on the first overflow after restart because the quota was already exhausted.Code gap
packages/subprocess/subprocess-local/src/spawn.ts:Called from:
discardSpill()/seal()already containcloseSync/unlinkSync(and tests mockfailNextClose/failNextUnlink).openSync/writeSyncinspillAllare uncaught. An exception from a'data'listener takes down the process.The package README already notes that completed spill files accumulate under
os.tmpdir()until something external cleans them. That accumulation is how a long-running web host can walk into the quota; the host death is the missing containment.Proposed fix (for maintainers; this is not a PR)
openSync/writeSyncinspillAll. On failure, call existingdiscardSpill(): disable further spilling, keep the in-memory tail. Same outcome as a spill that exceedsmaxSpillBytesor a failed finalclose.vi.mock('node:fs')inpackages/subprocess/subprocess-local/tests/spawn.spec.tswithfailNextOpen/failNextWrite. Assertpush()does not throw,spillPathis undefined, and the retained tail is still present. Cover both “open fails” and “open succeeds, first write fails” (the 0-byte file case).DSH_HOMEinstead ofos.tmpdir(), so a systemd tmpfs user quota cannot kill the host. Raising the OS quota is not a product fix.Non-goals
All reactions