#36049 streams: avoid termination on buffered write failure

ReviewabilityReviewsAgreementSize
#36049 l0rinc streams: avoid termination on buffered write failureWaiting on author1Strong19 + 36 tests
  • Prevent the node from abruptly crashing when disk writes fail during block storage
  • Enable clean error reporting and graceful shutdown instead of sudden process aborts
  • Reviewable immediately with nothing known that would invalidate a review
  • Support from a contributor who ran into the same bug while working in this area (w0xlt)
  • Asked why storage write errors should be caught instead of crashing, answered by author (andrewtoth)
  • Suggested finding a wholesale pattern or linter check for destructor exceptions (maflcko)
  • 19 lines added or modified outside tests
  • 36 lines added or modified in tests
  • 5 lines removed in total
  • 3 files, 3 commits
Utilities (logging, arguments, libraries)#2 of 59
P1 · bug fix
  • P1 after comparing with the other PRs here (assessed alone as P2): Raised to P1: an uncatchable std::terminate on buffered write failure is a reliability/data-safety failure observed in practice, not a routine bug fix.
  • P2 because it stops stream write failures from triggering an uncatchable process abort
  • Allows callers to safely catch and handle write errors during buffered I/O
Validation#8 of 53
P2 · bug fix
  • P2 because disk full conditions during block writing cleanly trigger storage error handling
  • Avoids unexpected node crashes when disk space runs out during block storage
  • Ranking pass: Moved up: author observed real std::terminate crashes on disk-full; most certain benefit of the error-handling group
Reviewability: ReadyStalePaused
Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets
Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs.
Size: lines added or modified outside tests, then in tests; greener = smaller.

Goal

Clear pending buffer bytes before invoking the underlying stream write in `BufferedWriter::flush()` to avoid re-attempting writes during exception unwinding. Explicitly call `flush()` within try-catch blocks in `BlockManager::WriteBlock` and `BlockManager::WriteBlockUndo` so disk write errors trigger clean fatal error handling instead of aborting the process.

Problem: When block or undo writes fail due to disk issues (such as a full filesystem), `AutoFile::write_buffer()` throws an exception. Because `BufferedWriter` previously flushed pending bytes in its implicitly `noexcept` destructor, write failures invoked `std::terminate` instead of normal node storage error handling, and a failed explicit `flush()` would re-attempt the write upon destructor unwinding.

Evidence of importance:

  • Author report: 'my 1 TB benchmarking servers have recently been filling up, so I've been seeing these failures more often.'
  • PR description provides a minimal reproduction script demonstrating that short writes previously resulted in process aborts rather than a clean exit with 'Failed to write genesis block'.
  • The bug was introduced in merged PR #31551, which added `BufferedWriter`.

Risk of a mistake: medium (validation). A flaw in block and undo write flush handling could lead to silent data loss or premature fatal errors during block storage.

Changed since the previous assessment: No substantial changes to the code or approach; commits now credit a co-author and refine unit test coverage.

Reviewability

Paused · Waiting on author

Open objection from maflcko with no author reply for 38 days; review would repeat what has been asked.

Waiting on (from the thread read):

  • author: response to maflcko's suggestion regarding wholesale handling of exceptions during destruction 2026-08-21
  • author: response to maflcko's suggestion on wholesale prevention of destructor exceptions 2026-08-21

Agreement

Strong

substantive support, no open objection (w0xlt)

Thread notes: Reclassified maflcko's comment from an objection to a suggestion since review-time concern is not a harm of merging, and added andrewtoth's answered question. / Added andrewtoth's answered question regarding termination vs exception handling. Reclassified maflcko's comment from an objection to a suggestion because inefficient use of review time is not a merging harm.

Objections, suggestions, and questions:

ReviewerTypeHarm, request, or questionStatusMust beAuthor repliedQuote
andrewtoth (member)question, approachwhy storage write errors should be handled rather than immediately terminating the processanswered · author explained that handling the exception allows reporting the error and clean shutdown rather than std::terminateansweredyes2026-08-20: 'Can you expand on why this is a problem? If there is a storage error, don't we have to crash anyways?'
Status from: 2026-08-20: l0rinc: 'Handling the exception lets the node report the storage error and run its normal shutdown cleanup.'
maflcko (member)suggestion, approachfind a code pattern or compiler/linter check to avoid exception-during-destruction issues wholesale rather than addressing them piecemealopenansweredno2026-08-21: 'It would be good to find a code pattern that avoids this class of problem wholesale. It doesn't seem a great use of human (or LLM) review time to spend on such issues one-by-one.'

Support:

  • w0xlt (contributor) (Concept ACK, 2026-09-05) [read: AutoFile, BufferedWriter]: encountered the same bug while working in this area and implemented an alternative fix to handle buffered write errors
  • w0xlt (contributor) (ACK, 2026-09-24): (no reason given) [not substantive]

Participants:

  • andrewtoth (member, question, 1 statements 2026-08-20 to 2026-08-20): asked why catching storage write errors is preferable to terminating immediately
  • maflcko (member, neutral, 1 statements 2026-08-21 to 2026-08-21): suggested addressing exception-during-destruction issues wholesale rather than piecemeal
  • w0xlt (contributor, support, 2 statements 2026-09-05 to 2026-09-24): ACKed cac9501; previously Concept ACKed after encountering the same bug

Files

36 lines under test/bench/ci.

  • src/test/streams_tests.cpp +36/-0
  • src/node/blockstorage.cpp +13/-3
  • src/streams.h +6/-2

Card

The short text the ranking pass compares across PRs.

Prevents bitcoind from abruptly crashing via std::terminate when block or undo writes fail due to disk storage errors such as a full disk. BlockManager now explicitly flushes buffered writers within try-catch blocks to route errors to standard shutdown procedures, and BufferedWriter clears pending buffer offsets before writing to avoid re-triggering writes during stack unwinding. The author observed repeated crashes on 1 TB benchmark servers running out of space and provided a reproducer demonstrating that write failures now exit cleanly.

Data

dossier JSON · extract JSON · model ledger, generated 2026-09-28T01:18, confidence high, input hash b0da451d7c7fc9db
thread read through 2026-09-24 (6 statements), head cac9501d35
labels: none

Categories

Utilities (logging, arguments, libraries)

P1 · bug fix (assessed alone as P2; Raised to P1: an uncatchable std::terminate on buffered write failure is a reliability/data-safety failure observed in practice, not a routine bug fix.)

Prevents `std::terminate` during stream destruction on write failures by ensuring `BufferedWriter` handles flush errors safely. The stream primitive's implicit `noexcept` destructor was terminating the process rather than allowing callers to handle write exceptions cleanly.

Membership: Modifies `BufferedWriter` in `src/streams.h` to clear pending buffer bytes before writing, avoiding destructor aborts during exception unwinding.

Factors: security/stability 2, bug 2, performance 0, user value 1, leverage 0

Validation

P2 · bug fix (ranking pass: Moved up: author observed real std::terminate crashes on disk-full; most certain benefit of the error-handling group)

Ensures disk write failures in block storage trigger clean fatal error handling rather than an abrupt process abort via `std::terminate`. As shown in the reproduction script, short writes to block files previously crashed the process instead of cleanly reporting storage errors.

Membership: Changes `src/node/blockstorage.cpp` error handling during `BlockManager::WriteBlock` and `BlockManager::WriteBlockUndo`.

Factors: security/stability 2, bug 2, performance 0, user value 1, leverage 0