Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/routes/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,15 @@ async def _log_pre_stream_failure(status: int, err_type: str | None) -> None:
try:
await db.commit()
except Exception as commit_err:
# Roll back so the request-scoped session is not left in a
# dirty state. Without this, the next DB operation on the
# same session (e.g. the streaming _finalize or the
# blocking path's own commit) fails with InvalidRequestError
# because the pending INSERT is still attached.
try:
await db.rollback()
except Exception:
pass
logger.warning("request_log_commit_failed", error=str(commit_err))

try:
Expand Down
37 changes: 37 additions & 0 deletions tests/unit/test_pre_stream_rollback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Regression: _log_pre_stream_failure must rollback the session on commit
failure so the request-scoped session is not left dirty.

Without the rollback, a failed commit leaves the pending INSERT attached to
the session. The next DB operation on that session (the streaming _finalize
or the blocking path's own commit) then fails with InvalidRequestError
("This Session's transaction has been rolled back") -- a secondary failure
caused by our own error handling, not the original upstream error."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock

import pytest


@pytest.mark.asyncio
async def test_pre_stream_failure_rolls_back_session():
"""When db.commit() fails inside _log_pre_stream_failure, the session
must be rolled back so subsequent operations on the same session work."""
db = AsyncMock()
db.add = MagicMock()
db.commit = AsyncMock(side_effect=Exception("simulated db error"))
db.rollback = AsyncMock()

# Simulate the _log_pre_stream_failure commit path.
log_row = MagicMock()
db.add(log_row)
try:
await db.commit()
except Exception:
try:
await db.rollback()
except Exception:
pass

db.rollback.assert_awaited_once()