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
3 changes: 3 additions & 0 deletions src/agents/extensions/memory/advanced_sqlite_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ def _add_items_sync():
except Exception as cleanup_error:
conn.rollback()
self._logger.error(f"Failed to cleanup orphaned messages: {cleanup_error}")
raise RuntimeError(
f"Failed to persist structure metadata for session {self.session_id}"
) from e

await asyncio.to_thread(_add_items_sync)

Expand Down
29 changes: 29 additions & 0 deletions tests/extensions/memory/test_advanced_sqlite_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import asyncio
import json
import sqlite3
import tempfile
from pathlib import Path
from typing import Any, cast
Expand Down Expand Up @@ -1396,6 +1397,34 @@ async def add_batch(worker_id: int) -> list[str]:
session.close()


async def test_add_items_raises_on_structure_metadata_failure(monkeypatch):
"""Verify that add_items raises when structure metadata insertion fails.

Regression test for #3348: add_items() previously swallowed the exception
and reported success even when structure metadata could not be persisted.
"""
session = AdvancedSQLiteSession(session_id="metadata_fail", create_tables=True)

# Force _insert_structure_metadata to raise.
def _raise(*args, **kwargs):
raise sqlite3.OperationalError("simulated metadata failure")

monkeypatch.setattr(session, "_insert_structure_metadata", _raise)

with pytest.raises(RuntimeError, match="Failed to persist structure metadata"):
await session.add_items([{"role": "user", "content": "hello"}])

# The orphaned message should have been cleaned up.
with session._locked_connection() as conn:
remaining = conn.execute(
f"SELECT COUNT(*) FROM {session.messages_table} WHERE session_id = ?",
(session.session_id,),
).fetchone()[0]

assert remaining == 0
session.close()


async def test_output_tokens_details_persisted_when_input_details_missing():
"""Regression: output_tokens_details must persist even if input_tokens_details is None.

Expand Down