Skip to content

Repository.close(): don't mask the original exception when unwinding with buffered chunks - #10013

Draft
ThomasWaldmann wants to merge 1 commit into
borgbackup:masterfrom
ThomasWaldmann:packwriter-drop-on-unwind
Draft

Repository.close(): don't mask the original exception when unwinding with buffered chunks#10013
ThomasWaldmann wants to merge 1 commit into
borgbackup:masterfrom
ThomasWaldmann:packwriter-drop-on-unwind

Conversation

@ThomasWaldmann

Copy link
Copy Markdown
Member

Problem

Repository.close() asserts that the PackWriter has no unflushed chunks. When a borg command aborts with chunks still buffered (e.g. an exception mid-borg create between pack flushes), the with repository: unwind calls close() and this assertion raises AssertionError, masking the original exception. Additionally, the buffered chunks left F_PENDING entries in the chunk index, which the close()-time write_chunkindex_to_repo would trip over (it asserts no entry is still pending).

Reproduce: open a Repository, put() one small chunk (below the pack size limit), raise inside the with-block — the AssertionError replaces the real error.

Fix

  • New PackWriter._drop_buffered(): empties the piece buffer and deletes the still-pending index entries for the buffered chunks. flush()'s store-failure path now reuses it instead of duplicating the same drop logic inline.
  • Repository.__exit__ calls it when unwinding an exception, before close(). The never-stored chunks die with the aborted operation: the original exception propagates unmasked, and no F_PENDING entries reach the persisted chunk index.
  • The assertion in close() is unchanged: on a clean close it still catches a genuinely forgotten flush(). __exit__ is the only place that knows whether we are unwinding, so the drop decision lives there.

Tests

  • test_exception_unwind_drops_buffered_chunks: the repro above; asserts the original ValueError (not AssertionError) propagates, and after reopening the buffered chunk is neither in the chunk index nor readable.
  • test_close_with_unflushed_chunks_asserts: documents that a clean exit with buffered chunks still trips the assertion.

All repository and cache tests pass; ruff clean.

🤖 Generated with Claude Code

…with buffered chunks

When a borg command aborts with chunks still buffered in the PackWriter
(e.g. an exception mid-create between pack flushes), the "with repository:"
unwind called close(), whose "PackWriter has unflushed chunks" assertion
raised AssertionError and masked the original exception. Also, buffered
chunks left F_PENDING entries in the chunk index, which the close()-time
index persist would trip over.

Fix: on exception unwind, Repository.__exit__ now drops the buffered
pieces and their still-pending index entries via the new
PackWriter._drop_buffered() (also reused in flush()'s store-failure
path), so the original exception propagates unmasked and no F_PENDING
entries are persisted. The never-stored chunks die with the aborted
operation. On a clean close, the assertion still catches a forgotten
flush().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ThomasWaldmann
ThomasWaldmann marked this pull request as draft August 2, 2026 14:48
@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (2a5cb4c) to head (3d0aff0).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/borg/repository.py 91.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master   #10013   +/-   ##
=======================================
  Coverage   86.10%   86.10%           
=======================================
  Files          96       96           
  Lines       17326    17332    +6     
  Branches     2649     2650    +1     
=======================================
+ Hits        14918    14924    +6     
  Misses       1667     1667           
  Partials      741      741           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant