Skip to content

[Bug]: RedisAdapter.get_latest_qa_entries returns None instead of [] for an empty session (last_n=1) #3930

Description

@shafi-VM

Bug Description

The three session-cache backends implement the same CacheDBInterface, and get_latest_qa_entries is annotated -> list[SessionQAEntry]. On an empty/unknown session the SQL and FS adapters return [] for every last_n. The Redis adapter does not: when last_n == 1 and the session has no entries it returns None.

The repo already treats []-on-empty as the intended contract and even names this divergence. In cognee/tests/unit/infrastructure/databases/cache/test_sql_adapter_crud.py:

async def test_empty_session_returns_empty_list_for_all_last_n(adapter):
    """[] on empty for every last_n value — including last_n=1 (FS-style, not Redis quirk)."""
    assert await adapter.get_latest_qa_entries("u1", "missing", last_n=1) == []

The SQL adapter was written to avoid the quirk, but the Redis adapter that has it was never fixed.

Root cause — cognee/infrastructure/databases/cache/redis/RedisAdapter.py, the last_n == 1 fast path of get_latest_qa_entries:

if last_n == 1:
    data = await self.async_redis.lindex(session_key, -1)
    return [SessionQAEntry.model_validate_json(data)] if data else None   # <-- None on empty
data = await self.async_redis.lrange(session_key, -last_n, -1)
return [SessionQAEntry.model_validate_json(d) for d in data] if data else []

last_n == 1 is a hot path: session_turn.py calls get_session(..., last_n=1) on every turn to load the previous entry, which routes to get_latest_qa_entries(..., last_n=1). SessionManager.get_session only survives an empty session on Redis today because of a defensive if entries is None: guard (cognee/infrastructure/session/session_manager.py:489) that exists purely to paper over this. Any other caller that iterates the result (including the backward-compat get_latest_qa, as the return type invites) hits TypeError: 'NoneType' object is not iterable on Redis while working fine on SQLite/FS.

Steps to Reproduce

  1. Configure the Redis cache backend (CACHE_BACKEND=redis).
  2. For a brand-new session with no stored QA, call await cache.get_latest_qa_entries(user_id, "new_session", last_n=1) (equivalently SessionManager.get_session(user_id=..., session_id="new_session", last_n=1)).
  3. The Redis backend returns None; the SQLite/FS backends return [].

Expected Behavior

get_latest_qa_entries returns [] on an empty/unknown session for every last_n, on every backend — matching its declared return type and the SQL/FS adapters (and the contract the SQL adapter test already documents).

Actual Behavior

On the Redis backend with last_n == 1, an empty session returns None, diverging from the other backends and the return-type annotation, and forcing downstream is None guards.

Environment

  • OS: macOS 15 (also reproducible on any OS)
  • Python version: 3.13
  • Cognee version: current dev
  • Cache backend: redis (CACHE_BACKEND=redis)

Logs/Error Messages

E       assert None == []
cognee/tests/unit/infrastructure/databases/cache/test_redis_adapter_crud.py: AssertionError

Additional Context

Scope note: the same method also returns all entries (rather than []) when last_n <= 0 on the Redis/FS backends (lrange(key, -0, -1) / entries[-0:]), whereas SQL returns []. That path is currently guarded upstream by SessionManager._validate_session_params (which rejects last_n < 1), so I'm treating it as out of scope and only fixing the documented last_n == 1 empty-session contract violation.

Pre-submission Checklist

  • I have searched existing issues to ensure this bug hasn't been reported already
  • I have provided a clear and detailed description of the bug
  • I have included steps to reproduce the issue
  • I have included my environment details

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions