You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Sep 17, 2026. It is now read-only.
_METADATA_LOAD_SEMAPHORE = asyncio.Semaphore(50) is a single module-level semaphore in src/solace_agent_mesh/agent/utils/artifact_helpers.py (line ~47). It is acquired by both get_artifact_info_list_fast and get_artifact_info_list (the latter via #1590).
The WebUI gateway runs more than one long-lived asyncio event loop in the same process — e.g. uvicorn's loop on the FastAPI_Thread (where HTTP routes call these helpers) and the SAC component's async_loop reached via run_coroutine_threadsafe (gateway/generic/component.pylist_artifacts → get_artifact_info_list). A single asyncio.Semaphore is bound (lazily, on first acquire) to one running loop; its internal waiter futures belong to that loop. If it is acquired from a second loop under contention, asyncio can raise RuntimeError: <future> ... is bound to a different event loop.
Why this is filed separately (not a blocker for #1590)
This is pre-existing — get_artifact_info_list_fast already uses the exact same shared semaphore the same way on main. #1590 only extends the established pattern to the per-session path (parity). It does not introduce or worsen the issue, so it shouldn't gate that PR.
Suggested fix (applies to BOTH functions)
Resolve the semaphore per running loop rather than once at import, e.g. a small helper that memoizes a Semaphore keyed on id(asyncio.get_running_loop()) (or a WeakKeyDictionary keyed by loop), and have both helpers acquire from that. Then the cap is enforced independently per loop and no future crosses loops.
Acceptance
get_artifact_info_list and get_artifact_info_list_fast both acquire a loop-local semaphore.
No bound to a different event loop errors when the per-session list path and the cross-session /api/v1/artifacts/all path run under different loops concurrently.
Summary
_METADATA_LOAD_SEMAPHORE = asyncio.Semaphore(50)is a single module-level semaphore insrc/solace_agent_mesh/agent/utils/artifact_helpers.py(line ~47). It is acquired by bothget_artifact_info_list_fastandget_artifact_info_list(the latter via #1590).The WebUI gateway runs more than one long-lived asyncio event loop in the same process — e.g. uvicorn's loop on the
FastAPI_Thread(where HTTP routes call these helpers) and the SAC component'sasync_loopreached viarun_coroutine_threadsafe(gateway/generic/component.pylist_artifacts→get_artifact_info_list). A singleasyncio.Semaphoreis bound (lazily, on firstacquire) to one running loop; its internal waiter futures belong to that loop. If it is acquired from a second loop under contention, asyncio can raiseRuntimeError: <future> ... is bound to a different event loop.Why this is filed separately (not a blocker for #1590)
This is pre-existing —
get_artifact_info_list_fastalready uses the exact same shared semaphore the same way onmain. #1590 only extends the established pattern to the per-session path (parity). It does not introduce or worsen the issue, so it shouldn't gate that PR.Suggested fix (applies to BOTH functions)
Resolve the semaphore per running loop rather than once at import, e.g. a small helper that memoizes a
Semaphorekeyed onid(asyncio.get_running_loop())(or aWeakKeyDictionarykeyed by loop), and have both helpers acquire from that. Then the cap is enforced independently per loop and no future crosses loops.Acceptance
get_artifact_info_listandget_artifact_info_list_fastboth acquire a loop-local semaphore.bound to a different event looperrors when the per-session list path and the cross-session/api/v1/artifacts/allpath run under different loops concurrently.Found during independent review of #1590.