Skip to content

[Bug]: Workflow URL download leaves a temp file after interrupt #4132

Description

@SebTardif

Bug Description

specify workflow add <url> writes the download to tempfile.NamedTemporaryFile(..., delete=False). Cleanup on failure is only in except Exception. Two paths skip that cleanup:

  1. except typer.Exit: raise re-raises without unlinking.
  2. KeyboardInterrupt (a BaseException) is not an Exception, so Ctrl+C during the size-limited read leaves the file in the system temp directory.

The later finally: tmp_path.unlink(...) only runs after a successful download, when install has started.

The comment next to the NamedTemporaryFile already says the path is assigned immediately so a failed read can still remove the file. The typer.Exit and BaseException paths were left out.

Steps to Reproduce

  1. Run specify workflow add with an HTTPS URL that starts streaming a large body (or mock read_response_limited to block).
  2. Press Ctrl+C while bytes are being written to the temp file.
  3. List the system temp directory for a leftover *.yml, *.download, or archive-suffixed file created by this process.

Alternatively, read src/specify_cli/workflows/_commands.py around the download try/except typer.Exit/except Exception block and the later install finally.

Expected Behavior

Any exit after the temp file is created (error, typer.Exit, or interrupt) should unlink it, matching the existing best-effort cleanup on other download errors.

Actual Behavior

Interrupt or typer.Exit during download leaves delete=False temp files behind.

Specify CLI Version

0.16.5.dev0 (main at bf88c9f9a82f)

AI Agent

Grok Build

Operating System

macOS 26.6.1

Python Version

Python 3.14.7

Additional Context

Suggested fix: catch BaseException for cleanup in the download try, or put download and install under one try/finally that always unlinks tmp_path. I can open a PR if wanted.

Posted on behalf of @SebTardif by Grok (model: grok-4.6).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions