Skip to content

Session pop_item can report empty after dropping corrupt newest item #3304

@Aphroq

Description

@Aphroq

Please read this first

  • Have you read the docs? Yes. This concerns the session backend pop_item() behavior rather than a documented usage question.
  • Have you searched for related issues? Yes. I searched upstream issues and PRs.

Describe the bug

pop_item() can return None after deleting a corrupted newest item even when older valid session history still exists. A caller can interpret None as an empty session and stop trimming or rewinding, while valid history remains in the backend.

The affected paths are the non-MongoDB session backends that remove the newest item before deserializing it, including SQLite, AsyncSQLite, Redis, SQLAlchemy, and Dapr. MongoDB already skips corrupted newest documents and continues to the next valid item after PR #3247.

Impact:

  • Session state can be misreported as empty.
  • A corrupted newest entry can hide older valid entries from callers using pop_item().
  • Recovery behavior differs across session backends for the same SessionABC operation.

Debug information

  • Agents SDK version: upstream main at bc3607ba
  • Python version: Python 3.12.1

Repro steps

Run this minimal script from a checkout with the repository development dependencies installed. It uses fakeredis so no external Redis server is required.

import asyncio

import fakeredis.aioredis

from agents.extensions.memory.redis_session import RedisSession


async def main() -> None:
    client = fakeredis.aioredis.FakeRedis()
    session = RedisSession("corrupt-pop", redis_client=client, key_prefix="test")

    valid = {"role": "user", "content": "valid older item"}
    await session.add_items([valid])
    await client.rpush("test:corrupt-pop:messages", "not valid json {{{")

    popped = await session.pop_item()
    remaining = await session.get_items()

    print(f"popped: {popped!r}")
    print(f"remaining: {remaining!r}")


asyncio.run(main())

Actual result on upstream main at bc3607ba:

popped: None
remaining: [{'role': 'user', 'content': 'valid older item'}]

Expected behavior

pop_item() should discard corrupted newest items and continue looking for the next valid item. It should return None only when no valid item remains after corrupted entries are removed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions