You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Uncaught Exception: Error: ENOSPC: no space left on device, o #61301
pen '/Users/praveenkumar/Library/Application Support/ai.opencode.desktop/window-state-36ea1fd4-80b5-4837-9d61-a311aa582ad2.json.tmp'
at writeFileSync (node:fs:2482:20)
at BrowserWindow.c (file:///Applications/OpenCode.app/Contents/Resources/app.asar/out/main/index.js:1:54541)
at BrowserWindow.emit (node:events:526:24)
Thanks for the report. I was able to reproduce this.
When a desktop window closes, the app saves its position and size to window-state-<id>.json in the app data folder. It writes a .tmp file first, then renames it over the real file. Nothing catches an error from that write, so when the disk is full the ENOSPC error escapes from the window's close event. Electron then shows it as the "Uncaught Exception" dialog you saw.
I couldn't run the macOS desktop app here. Instead, the script below runs the same window-state code against a write target that always fails with "no space left on device" and fires the window's close events. It throws the same ENOSPC error from writeFileSync as in your trace, on v2.2.5 and on the latest dev (1bf08ae).
The only thing lost is the saved window position and size. Freeing some disk space makes the dialog go away. It's related to #51470, where the same save fails with EPERM on Windows.
Reproduction script (window-state-enospc.ts)
// Reproduces #61301: the desktop window-state "closed" handler throws when the disk is full.// Run from the repo root of a dev checkout: bun /workspace/repro/window-state-enospc.ts [/path/to/repo]// The `${file}.tmp` path is a symlink to /dev/full, so writeFileSync fails with a real ENOSPC (Linux).import{EventEmitter}from"node:events"import{mkdtempSync,symlinkSync,existsSync}from"node:fs"import{tmpdir}from"node:os"importpathfrom"node:path"constrepo=process.argv[2]??"/workspace/repo"const{ manageWindowState, windowStateFile }=awaitimport(path.join(repo,"packages/desktop/src/main/windows/window-state.ts"))// Minimal stand-in for Electron's BrowserWindow (an EventEmitter with the getters the module uses).classFakeWindowextendsEventEmitter{isDestroyed=()=>falsegetBounds=()=>({x: 10,y: 10,width: 800,height: 600})isMaximized=()=>falseisMinimized=()=>falseisFullScreen=()=>falsemaximize(){}setFullScreen(){}}constdisplay={x: 0,y: 0,width: 1920,height: 1080}constdisplays={all: ()=>[display],primary: ()=>display,matching: ()=>display}constuserData=mkdtempSync(path.join(tmpdir(),"ai.opencode.desktop-"))constfile=path.join(userData,windowStateFile("36ea1fd4-80b5-4837-9d61-a311aa582ad2"))if(!existsSync("/dev/full"))thrownewError("needs /dev/full (Linux) to simulate a full disk")symlinkSync("/dev/full",`${file}.tmp`)// every write to window-state-*.json.tmp now gets ENOSPCconstwin=newFakeWindow()manageWindowState(winasany,file,{width: 800,height: 600},displays)win.emit("close")try{// Electron emits "closed" itself; an exception thrown by a listener there becomes// the "Uncaught Exception" dialog in the main process.win.emit("closed")console.log("OK: closed handler did not throw (bug not present)")}catch(err){console.log("BUG REPRODUCED: 'closed' listener threw:")console.log(String((errasError).stack).split("\n").slice(0,4).join("\n"))process.exitCode=1}
I'd like to take this if nobody is working on it. After checking dev, I verified that a real write failure in a temporary directory escapes the closed callback and prevents later close listeners from running. My local case is EISDIR, not a full disk.
I'll keep the fix to error handling around window-state saving, retain diagnostics, and add coverage for normal saves and continuing to close after a save failure. #54530 handles Windows lock retries on the same path; I won't duplicate its write or retry logic.
Activity
opencodejr commentedon Nov 2, 2026
Thanks for the report. I was able to reproduce this.
When a desktop window closes, the app saves its position and size to
window-state-<id>.jsonin the app data folder. It writes a.tmpfile first, then renames it over the real file. Nothing catches an error from that write, so when the disk is full theENOSPCerror escapes from the window's close event. Electron then shows it as the "Uncaught Exception" dialog you saw.I couldn't run the macOS desktop app here. Instead, the script below runs the same window-state code against a write target that always fails with "no space left on device" and fires the window's close events. It throws the same
ENOSPCerror fromwriteFileSyncas in your trace, on v2.2.5 and on the latestdev(1bf08ae).The only thing lost is the saved window position and size. Freeing some disk space makes the dialog go away. It's related to #51470, where the same save fails with
EPERMon Windows.Reproduction script (window-state-enospc.ts)
Run it:
bun window-state-enospc.tsLuxe7 commentedon Nov 3, 2026
I'd like to take this if nobody is working on it. After checking dev, I verified that a real write failure in a temporary directory escapes the closed callback and prevents later close listeners from running. My local case is EISDIR, not a full disk.
I'll keep the fix to error handling around window-state saving, retain diagnostics, and add coverage for normal saves and continuing to close after a save failure. #54530 handles Windows lock retries on the same path; I won't duplicate its write or retry logic.
Working on this with Codex assistance.