Skip to content

Cancelling URL audio downloads can leave partial files #1197

Description

@hsusul

Bug

Cancelling a URL audio download can leave an incomplete ow-url-* destination
behind after the operation has already reported cancellation.

fs.createWriteStream() opens its destination asynchronously. The existing
failure path destroys the stream and immediately attempts to unlink the
destination. If the stream has not finished opening, the unlink can observe
ENOENT. The pending open may then create the file after cleanup has
completed.

The problem is load-sensitive with an ordinary filesystem but can be
reproduced deterministically by delaying the write stream's open callback.

Minimal reproduction

  1. Start a direct URL audio download using a write stream whose filesystem
    open callback is delayed.
  2. Cancel the download before the delayed open completes.
  3. Allow the current cleanup path to attempt destination deletion.
  4. Release the delayed open callback.
  5. Observe that cancellation can settle before the stream closes and that the
    destination may be created after cleanup.

Expected behavior

Cancellation should not settle until:

  1. the destination stream reaches a terminal closed state; and
  2. the incomplete destination has been removed.

Actual behavior

Cleanup calls destroy() and immediately attempts deletion. destroy() does
not mean that the asynchronous file-open and close lifecycle has completed
when the call returns.

Verified root cause

The destination unlink is ordered before confirmed write-stream termination.
A pending open can therefore occur after the deletion attempt.

Proposed scope

  • Begin waiting for terminal write-stream completion before requesting
    destruction.
  • Wait for the stream to close.
  • Remove the incomplete destination afterward.
  • Ignore ENOENT while preserving visibility into unexpected filesystem
    failures.
  • Preserve the original cancellation or download error.
  • Add deterministic regression tests for delayed open and adjacent lifecycle
    cases.

Non-goals

  • Changing download retry policy
  • Changing progress reporting
  • Changing URL validation or download sources
  • Refactoring unrelated downloader code
  • Changing successful-download behavior
  • Changing model or runtime binary downloads
  • Adding dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions