Repository navigation
Description
Description
What happened?
AgentState.get_or_create_session() keeps a per-session asyncio.Lock in _session_locks for every distinct session ID passed to it.
The lock is created with:
session_lock = self._session_locks.setdefault(session_id, asyncio.Lock())
_session_locks is a regular dictionary, and completed locks are not removed. As a result, the lock registry can continue growing as new session IDs are processed, even after the corresponding sessions have been deleted from the SessionStore.
For example, after creating and deleting 1,000 unique sessions, the session store is empty but _session_locks still contains 1,000 entries.
What did you expect to happen?
Per-session synchronization state should not be retained indefinitely once there are no active users of the lock.
The existing concurrency behavior should remain unchanged: concurrent callers for the same session ID should continue to share the same lock so that the underlying session is created only once.
Steps to reproduce the issue
Create an AgentState.
Call get_or_create_session() with many unique session IDs.
Delete each corresponding session from the session store.
Inspect _session_locks.
Observe that the lock entries remain even though the sessions have been deleted.
A regression test using 1,000 unique session IDs consistently reproduces the issue.
Code Sample
async def test_get_or_create_session_does_not_retain_locks_for_completed_sessions(self) -> None:
agent = _FakeAgent()
state = AgentState(agent)
for i in range(1000):
session_id = f"session-{i}"
await state.get_or_create_session(session_id)
await state.session_store.delete(session_id)
assert len(state.session_store._sessions) == 0
assert len(state._session_locks) == 0Error Messages / Stack Traces
assert len(state._session_locks) == 0
AssertionError: assert 1000 == 0
1 failed, 23 deselectedPackage Versions
agent-framework-hosting: repository main (source checkout) agent-framework-core: repository main (source checkout)
Python Version
Python 3.14.8
Additional Context
The issue was reproduced against the current repository source.
A candidate fix using weakly referenced per-session lock values was also tested locally. This allows unused locks to be released while preserving strong references to locks that are actively being used or awaited.
Validation after the candidate change:
- New regression test passes.
- Existing
test_get_or_create_session_creates_once_for_concurrent_callerstest passes, preserving same-session concurrency behavior. - Full
packages/hosting/tests/hosting/test_state.pysuite:24 passed. ruff checkpasses.ruff format --checkpasses.git diff --checkreports no errors.
The candidate change does not alter the SessionStore lifecycle or public API.
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
github-actions commentedon Oct 7, 2026
🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
The bug reproduces in
python/packages/hosting/agent_framework_hosting/_state.py::AgentState.get_or_create_sessionat line 173 when many distinct session IDs are created and later deleted. Minimal repro: create anAgentState, callget_or_create_session()andsession_store.delete()for 1,000 unique IDs, then observe an empty store but 1,000 entries in_session_locks.python/packages/hosting/tests/hosting/test_state.py1.0.0a261002