Skip to content

inbox: cancelled Export leaves an orphaned 0-byte .crswap temp file instead of a recoverable partial artifact #105

Description

@BorisTyshkevich

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    inboxFiled mid-task; not yet triaged into the roadmap

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions