diff --git a/src/agents/extensions/memory/advanced_sqlite_session.py b/src/agents/extensions/memory/advanced_sqlite_session.py index 83c289bdf8..88a6233b92 100644 --- a/src/agents/extensions/memory/advanced_sqlite_session.py +++ b/src/agents/extensions/memory/advanced_sqlite_session.py @@ -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) diff --git a/tests/extensions/memory/test_advanced_sqlite_session.py b/tests/extensions/memory/test_advanced_sqlite_session.py index ad4b5c4d86..8c6235a8f6 100644 --- a/tests/extensions/memory/test_advanced_sqlite_session.py +++ b/tests/extensions/memory/test_advanced_sqlite_session.py @@ -2,6 +2,7 @@ import asyncio import json +import sqlite3 import tempfile from pathlib import Path from typing import Any, cast @@ -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.