Spotted while explaining the streaming Export's on-disk mechanics (#87/#103) to a user who couldn't find their exported file after clicking Cancel.
Where: src/ui/app.js streamToFile(), the catch block at (around) line 1110:
} catch (e) {
await writable.abort().catch(() => {}); // leave the partial file; report upstream
throw e;
}
Observed (macOS, Chrome): the File System Access API writes to a hidden swap file next to the target path (.com.google.Chrome.XXXXXXX.crswap) while streaming, and only swaps it into the user-visible filename on a successful writable.close(). On writable.abort() — hit on both user Cancel and a mid-stream server error — Chrome does not delete the swap file as the inline comment assumes. It truncates it to 0 bytes and leaves it behind, hidden (dot-prefixed) and empty. Net effect: cancelling an export leaves neither the visible target file the user asked to save to, nor any recoverable partial data — just an invisible zero-byte orphan that silently accumulates in whatever folder the user picked.
Why deferred: this is browser-level File System Access API behavior discovered during user support, not in scope for any open issue; fixing it is a deliberate UX decision (what should "cancel" leave behind?), not a one-line patch.
Suggested fix shape: on cancel/mid-stream error, instead of writable.abort(), writable.close() the bytes already committed so far (so the partial content actually lands under the target handle), then use FileSystemFileHandle.move() (Chrome 110+) to rename it in place with a distinguishing suffix, e.g. <name>.<ext>.partial — so a cancelled/failed export leaves a clearly-labeled, inspectable partial file instead of a hidden empty one. Would need a fallback (or documented no-op) on browsers without .move(). Also fix/remove the now-inaccurate "leave the partial file" comment either way.
Spotted while explaining the streaming Export's on-disk mechanics (#87/#103) to a user who couldn't find their exported file after clicking Cancel.
Where:
src/ui/app.jsstreamToFile(), the catch block at (around) line 1110:Observed (macOS, Chrome): the File System Access API writes to a hidden swap file next to the target path (
.com.google.Chrome.XXXXXXX.crswap) while streaming, and only swaps it into the user-visible filename on a successfulwritable.close(). Onwritable.abort()— hit on both user Cancel and a mid-stream server error — Chrome does not delete the swap file as the inline comment assumes. It truncates it to 0 bytes and leaves it behind, hidden (dot-prefixed) and empty. Net effect: cancelling an export leaves neither the visible target file the user asked to save to, nor any recoverable partial data — just an invisible zero-byte orphan that silently accumulates in whatever folder the user picked.Why deferred: this is browser-level File System Access API behavior discovered during user support, not in scope for any open issue; fixing it is a deliberate UX decision (what should "cancel" leave behind?), not a one-line patch.
Suggested fix shape: on cancel/mid-stream error, instead of
writable.abort(),writable.close()the bytes already committed so far (so the partial content actually lands under the target handle), then useFileSystemFileHandle.move()(Chrome 110+) to rename it in place with a distinguishing suffix, e.g.<name>.<ext>.partial— so a cancelled/failed export leaves a clearly-labeled, inspectable partial file instead of a hidden empty one. Would need a fallback (or documented no-op) on browsers without.move(). Also fix/remove the now-inaccurate "leave the partial file" comment either way.