Summary
DefaultSessionStore.list_sessions() (inherited by HierarchicalSessionStore) crashes with a TypeError when any session has updated_at=None, because the sort key falls back to an empty string for missing keys but does not coerce explicit None values.
Discovered while validating #1444 (aiui dashboard datastore adapter). The adapter catches the exception and returns [], but the root cause is in the Core SDK.
Reproducer
import asyncio
from praisonaiagents.session import get_hierarchical_session_store
store = get_hierarchical_session_store()
# Add a session, then manually set updated_at=None (or trigger a path that leaves it None)
# store.list_sessions(limit=50) → TypeError
Actual traceback seen:
File "praisonaiagents/session/store.py", line 493, in list_sessions
sessions.sort(key=lambda x: x.get("updated_at", ""), reverse=True)
TypeError: '<' not supported between instances of 'NoneType' and 'str'
Root cause
praisonaiagents/session/store.py:493:
sessions.sort(key=lambda x: x.get("updated_at", ""), reverse=True)
dict.get(key, default) only uses default when the key is missing, not when the stored value is None. Any session whose metadata contains "updated_at": None breaks the sort.
Minimal fix (single line)
sessions.sort(key=lambda x: x.get("updated_at") or "", reverse=True)
The or "" coerces both missing-key (None from .get()) and explicit-None to "".
Same pattern likely needed in any sibling sort — grep sessions.sort / .sort(key=lambda in session/store.py and fix uniformly.
Acceptance criteria
Why this matters
Exposed by merged PR #1444 — the aiui dashboard's Sessions page uses PraisonAISessionDataStore.list_sessions() which delegates here. Until this is fixed, the dashboard will silently log ERROR Failed to list sessions and show an empty Sessions pane whenever a session has updated_at=None, which users will perceive as data loss.
References
Summary
DefaultSessionStore.list_sessions()(inherited byHierarchicalSessionStore) crashes with aTypeErrorwhen any session hasupdated_at=None, because the sort key falls back to an empty string for missing keys but does not coerce explicitNonevalues.Discovered while validating #1444 (aiui dashboard datastore adapter). The adapter catches the exception and returns
[], but the root cause is in the Core SDK.Reproducer
Actual traceback seen:
Root cause
praisonaiagents/session/store.py:493:dict.get(key, default)only usesdefaultwhen the key is missing, not when the stored value isNone. Any session whose metadata contains"updated_at": Nonebreaks the sort.Minimal fix (single line)
The
or ""coerces both missing-key (Nonefrom.get()) and explicit-Noneto"".Same pattern likely needed in any sibling sort — grep
sessions.sort/.sort(key=lambdainsession/store.pyand fix uniformly.Acceptance criteria
list_sessions()returns results sorted byupdated_atdesc, withNone/ missing treated as lowest priority.updated_at=Nonein its metadata, calllist_sessions(), assert it returns without raising and theNonesession appears last.list_sessions_by_agent(line 498) andlist_sessions_by_gateway_agent(line 637) if they have the same sort.Why this matters
Exposed by merged PR #1444 — the
aiuidashboard's Sessions page usesPraisonAISessionDataStore.list_sessions()which delegates here. Until this is fixed, the dashboard will silently logERROR Failed to list sessionsand show an empty Sessions pane whenever a session hasupdated_at=None, which users will perceive as data loss.References
praisonaiagents/session/store.py:469-497(list_sessions)AGENTS.md §4.6 Invariants: deterministic tests, safe defaults