Skip to content

fix: roll back session uploads on client disconnect and cancellation - #19

Merged
cchacs merged 1 commit into
mainfrom
pullfrog/17-roll-back-session-uploads
Oct 11, 2026
Merged

cchacs merged 1 commit into
mainfrom
pullfrog/17-roll-back-session-uploads

Conversation

@pullfrog

@pullfrog pullfrog Bot commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Closes #17.

Problem

POST /sessions/{id}/video and POST /sessions/{id}/audio only rolled back on OSError/ValueError. A stream that ends with Starlette's ClientDisconnect (client drops mid-append) or an asyncio.CancelledError (task cancelled) bypassed both handlers:

  • frames.h264 kept the partial bytes, while the session's video_bytes counter stayed at its pre-request value — a retry could corrupt the elementary stream and the cumulative 2 GB cap no longer matched disk contents.
  • A half-uploaded audio.bin remained, so the next audio upload was refused with 409 even though nothing usable had been stored.

ClientDisconnect subclasses Exception, not OSError/ValueError; CancelledError subclasses BaseException. Both are therefore invisible to a narrower handler.

Fix

routes.py:

  • The video append now catches BaseException for its rollback: on any unsuccessful exit it restores video_bytes to the accepted prefix and truncates or unlinks frames.h264. The rollback stays inside append_lock, so a queued append cannot snapshot the counter mid-rollback. The exception is re-raised.
  • The audio upload removes the incomplete mix on any BaseException, then re-raises.
  • Re-raising preserves cancellation semantics — cleanup is fully synchronous, so it cannot swallow or delay the cancellation.

The existing OSError/ValueError outer handling (400 responses) is unchanged.

Tests

Four new pytest.mark.asyncio tests call the registered endpoints directly with a request stream that yields a chunk and then disconnects or blocks until cancelled:

  • test_disconnected_video_stream_rolls_back_to_accepted_prefix and test_cancelled_video_stream_rolls_back_and_preserves_cancellation — assert the exact file contents after rollback, and prove the counter was restored by a follow-up 5-byte append that only fits if the snapshot is correct.
  • test_disconnected_audio_stream_is_removed_and_retry_succeeds and test_cancelled_audio_stream_is_removed_and_retry_succeeds — assert audio.bin is gone and a normal upload retry succeeds.

All four were verified to fail against the unpatched routes.py and pass against the fix. Full suite: 37 passed (33 pre-existing + 4 new).

plugin.json bumped to 0.3.2; CHANGELOG.md and CLAUDE.md updated.

Note

The audio route's exists()→open("wb") guard remains non-atomic (pre-existing); overlapping uploads could still race, and the new unlink slightly widens that window. The browser uploads audio at most once per session, so this is out of scope for this fix.

Pullfrog  | View workflow run | via Pullfrog | Using big-pickle (free) | 𝕏

append_session_video and upload_session_audio only cleaned up on OSError/ValueError, so a stream that ended with starlette's ClientDisconnect or a task cancellation left partial bytes behind: frames.h264 kept the extra chunk while the byte counter stayed at its pre-request value, and a half-uploaded audio.bin blocked a retry with 409.

Roll video back to its accepted prefix (and restore the counter) and remove the incomplete audio on every unsuccessful stream exit, then re-raise so cancellation still propagates. Add interrupted-stream tests covering both disconnect and cancellation, including a successful audio retry.
@kilo-code-bot

kilo-code-bot Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (5 files)
  • routes.py - Core fix: rollback on BaseException for video/audio uploads
  • tests/test_routes.py - 4 new async tests for disconnect/cancellation scenarios
  • CHANGELOG.md - Release notes for the fix
  • CLAUDE.md - Updated architecture documentation
  • plugin.json - Version bump to 0.3.2

Reviewed by free · Input: 124.3K · Output: 5.1K · Cached: 64.8K

@cchacs
cchacs merged commit 30180b9 into main Oct 11, 2026
5 checks passed
@cchacs
cchacs deleted the pullfrog/17-roll-back-session-uploads branch October 11, 2026 02:21
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.

Roll back session uploads on client disconnect and cancellation

1 participant