Skip to content

.quarto_ipynb files not cleaned up when preview is killed ungracefully #14359

@cderv

Description

@cderv

Reported by @mine-cetinkaya-rundel.

When using Positron and clicking the trash/bin icon on the terminal pane running quarto preview, the process is killed without clean shutdown. The transient .quarto_ipynb file created during render is not cleaned up. On the next preview, a new .quarto_ipynb_1 is created, and this accumulates (_2, _3, ...) each time the user repeats the pattern.

This is distinct from #14281, which fixed accumulation within a single preview session (re-renders). This issue is about accumulation across sessions when the process exits ungracefully.

Steps to reproduce

Create test.qmd with Jupyter engine content:

---
format: html
---

```{python}
1 + 1
```
  1. Open in Positron
  2. Click the Preview button (opens terminal running quarto preview test.qmd)
  3. Wait for render to complete
  4. Click the bin/trash icon on the terminal pane (kills terminal)
  5. Click Preview again
  6. Repeat steps 4-5 a few times

Actual behavior

Files accumulate:

test.quarto_ipynb
test.quarto_ipynb_1
test.quarto_ipynb_2

Expected behavior

No .quarto_ipynb files left behind between preview sessions.

Analysis

In quarto render, the .quarto_ipynb is cleaned up promptly after render completes — cmd.ts calls context.cleanup() which triggers cleanupFileInformationCache()invalidateForFile()safeRemoveSync().

In quarto preview, cleanup is deferred: the file sits on disk between renders and is only deleted at process exit (via onCleanup) or before the next re-render (via invalidateForFile). If the process is killed ungracefully (SIGKILL, taskkill, terminal destruction), the exit cleanup never fires.

Historically, cleanupNotebook() in jupyter.ts used to delete the file immediately after execution when keep-ipynb was not set. This was changed in #12793 to integrate with the fileInformationCache system (fixing #12780), but the immediate deletion for the keep-ipynb: false case was dropped as a side effect. The file deletion was fully delegated to the cache cleanup, which only runs at context disposal time.

Suggested fix

Restore immediate deletion in cleanupNotebook() for the keep-ipynb: false case, while preserving the cache integration for keep-ipynb: true:

This restores the original behavior from before #12793 and aligns preview with how quarto render handles cleanup. The execute() function already has a guard that recreates the file if missing (with a comment: "could have been removed by the cleanup step of another render"), so re-renders continue to work.

Related: #14281, #12780, #12793, #11597

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpreviewissues related to the `preview` command

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions