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
fix(tui): retry writeJsonAtomic on transient EPERM rename collisions on Windows #61119
In packages/tui/src/util/persistence.ts, writeJsonAtomic creates a temporary file and replaces the destination using rename(temporary, filePath).
On Windows NTFS, atomic renames fail immediately with Error: EPERM: operation not permitted when another handle (such as anti-virus, indexing, or a concurrent async tick) holds an open handle or is replacing the destination file. Because packages/tui/src/context/local.tsx calls void writeJsonAtomic(filePath, ...), this rejection is unhandled and logs:
Thanks for the clear report. I couldn't reproduce this here because it needs Windows file locking. This sandbox is Linux, where renaming over a file another process has open always succeeds, so the EPERM can't happen. A maintainer with a Windows machine will need to confirm it.
On the latest dev (8bca532), writeJsonAtomic in packages/tui/src/util/persistence.ts still renames the temporary file over session.json only once, with no retry. If the rename fails, it deletes the temporary file and passes the error on. The session.json write in packages/tui/src/context/local.tsx doesn't catch that error, which fits the unhandled rejection and the lost write you're seeing.
@opencodejr Thanks for confirming the mechanism on dev (8bca532).
Indeed, on Windows NTFS, file handles held by anti-virus scanners, Windows search indexers, or rapid concurrent async ticks cause atomic renames to fail with EPERM rather than waiting. Adopting the exact retry-with-backoff pattern already implemented for the desktop app's state persistence in #51470 / #54530 will make writeJsonAtomic resilient on win32.
Activity
opencodejr commentedon Nov 2, 2026
Thanks for the clear report. I couldn't reproduce this here because it needs Windows file locking. This sandbox is Linux, where renaming over a file another process has open always succeeds, so the
EPERMcan't happen. A maintainer with a Windows machine will need to confirm it.On the latest
dev(8bca532),writeJsonAtomicinpackages/tui/src/util/persistence.tsstill renames the temporary file oversession.jsononly once, with no retry. If the rename fails, it deletes the temporary file and passes the error on. Thesession.jsonwrite inpackages/tui/src/context/local.tsxdoesn't catch that error, which fits the unhandled rejection and the lost write you're seeing.Related:
EPERMon thesession.jsonrename on Windows. It was closed, but the code still has no retry.EPERMin the desktop app's settings and window-state files, with a PR that adds a retry.Bearmancer commentedon Nov 2, 2026
@opencodejr Thanks for confirming the mechanism on dev (8bca532).
Indeed, on Windows NTFS, file handles held by anti-virus scanners, Windows search indexers, or rapid concurrent async ticks cause atomic renames to fail with EPERM rather than waiting. Adopting the exact retry-with-backoff pattern already implemented for the desktop app's state persistence in #51470 / #54530 will make writeJsonAtomic resilient on win32.