Repository navigation
Conversation
Catch window-state filesystem errors so later closed listeners still run. Co-authored-by: Codex <noreply@openai.com>
Exercise real filesystem failures and verify later close listeners still run. Co-authored-by: Codex <noreply@openai.com>
There was a problem hiding this comment.
I reproduced #61301 on the base with the issue's /dev/full script: the closed listener throws ENOSPC. With this change it no longer throws, the save failure is logged, and the window keeps closing. The desktop tests for this file pass, and the new failure cases fail on the base. tsc -b on the desktop package is clean.
Public API: unchanged.
Two things are worth addressing (details inline):
- Logging:
console.warnbypasses the desktop logger, so this failure won't appear in the log file or the debug export. - Test size: the new test could be much smaller.
A partial .tmp left after a failed write is harmless, because the next save overwrites it.
This will conflict with #54530, which replaces these same three lines with writeFileAtomic. Whichever lands second should keep the try/catch around the new call, because that helper still rethrows non-lock errors such as ENOSPC.
Related: #51470
| writeFileSync(`${file}.tmp`, JSON.stringify(state)) | ||
| renameSync(`${file}.tmp`, file) | ||
| } catch (error) { | ||
| console.warn("Failed to save window state", { file, error }) |
There was a problem hiding this comment.
The rest of the desktop main process logs through the Effect logger backed by electron-log. For example, windows/index.ts uses scoped("window", Effect.logError("failed to clean window state", ...)). console.warn doesn't go through that, so in a packaged app this failure won't be in the log file or in "export debug logs", and that's where we'd look for it. Could the caller in windows/index.ts pass in a way to report the error? For example, a small onError that does runFork(scoped("window", Effect.logWarning("failed to save window state", { file, error }))).
| }) | ||
|
|
||
| describe("saving window state on close", () => { | ||
| test.each(["success", "mkdir", "write", "rename", ...(process.platform === "linux" ? ["full"] : [])])( |
There was a problem hiding this comment.
This one table runs five cases, each with its own if (stage === ...) setup and assertions, so it's hard to see what each case checks. All four failure cases go through the same catch. Could you cut this down to a single direct test: a failing save (e.g. the .tmp path being a directory), where emitting closed doesn't throw, a later closed listener still runs, and the previous state file is kept? The success case is already covered by the existing read/resolve tests, and the /dev/full case doesn't add anything beyond that.
Report save errors through the caller and upgrade early-window logging on adoption. Keep one focused filesystem regression test. Co-authored-by: Codex <noreply@openai.com>
There was a problem hiding this comment.
Both earlier points are addressed:
- Save failures now go through the desktop logger (
scoped("window", Effect.logWarning(...))). The early window switches to it once adopted. - The test is now one direct case.
I re-ran the issue's /dev/full reproduction. It still throws ENOSPC on the base. With this change closing continues, and the error reaches the callback. bun test src/main/windows/ passes, and tsc -b on the desktop package is clean.
Public API: unchanged. The new manageWindowState callback is internal to the desktop package.
No further concerns.
Issue for this PR
Closes #61301
Type of change
What does this PR do?
A failed window-state save throws out of the
closedcallback and prevents later close listeners from running. Catch the save error so closing can continue. The caller reports it through the desktop logger; early windows switch to that logger when adopted.#54530 touches the same code to retry Windows file locks. This patch handles the remaining save failure and does not add retry logic.
Implemented and tested with Codex assistance.
How did you verify your code works?
A real temporary-directory test makes the temporary-file path a directory. It verifies that closing does not throw, a later close listener runs, the error is reported, and the previous state file is preserved.
On macOS with Bun 1.4.4: desktop tests pass (232 passed, 4 platform-specific skips), the focused file passes ten repetitions, root
bun run checkandbun run lint:changed origin/devpass, and desktopbun run typecheck:tscpasses. No manual Electron UI test.Visual
Checklist
Added by the opencode bot (AI-generated).
Related: #51470