fix(mattermost): use per-call aiohttp ClientSession to avoid cross-loop bug - #35343
Closed
idarius wants to merge 1 commit into
Closed
fix(mattermost): use per-call aiohttp ClientSession to avoid cross-loop bug#35343idarius wants to merge 1 commit into
idarius wants to merge 1 commit into
Conversation
…op bug
The Mattermost adapter cached an aiohttp.ClientSession in connect() and
reused it across every REST helper. When send_message is dispatched from
the agent via model_tools._run_async — which runs the coroutine in a
fresh event loop on a worker thread — the cached session is consumed
cross-loop, and aiohttp.helpers raises:
RuntimeError: Timeout context manager should be used inside a task
Replace the eight HTTP call sites with a new _open_session(timeout_sec)
helper that creates a per-call ClientSession bound to the current event
loop. self._session is kept for the WebSocket only (same loop as
connect(), no cross-loop issue).
The helper short-circuits when self._session is a non-aiohttp object
(test mocks), routing through _PassthroughSessionCM so the 43 existing
Mattermost tests keep intercepting HTTP calls unchanged.
Pattern follows #1736 (session-per-send for SMS, merged).
19 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes
Plugin platform send failed: Timeout context manager should be used inside a taskraised whenever an agent invokessend_message(or any cross-channel send) into a Mattermost channel from inside the gateway event loop.Root cause
MattermostAdapter.connect()cached oneaiohttp.ClientSessiononself._session, bound to the gateway's main event loop. Every REST helper reused that cached session.When
send_messageis dispatched from the agent,model_tools._run_asyncdetects the running gateway loop and spins up a fresh event loop on a worker thread to run the coroutine — seemodel_tools.pylines 84–143. The cached session is consumed cross-loop, andaiohttp.helpers.TimerContext.__enter__raises:aiohttp's check is
asyncio.current_task(loop=self._loop)against the session's loop; the worker-loop bridge has no current task in the gateway loop, so the check fails.The bug exists since the Mattermost plugin was created; it's been latent because most replies go through the gateway dispatch path (not the
send_messagetool). It surfaces consistently for cron deliveries and agent-initiated cross-channel sends.Related Issue
Fixes the runtime described above. No tracking issue was open for Mattermost specifically. Same architectural class as the weixin reports #33445, #31853, #17468, #17267, #18417, #28835, #35237 (Mattermost variant only here).
Type of Change
Changes Made
plugins/platforms/mattermost/adapter.py:_open_session(timeout_sec)— returns a context manager for a freshaiohttp.ClientSessionbound to the current event loop. Test fixtures that pre-loadself._session = MagicMock()are routed through_PassthroughSessionCMso existing tests keep intercepting HTTP calls without modification._open_sessioninstead of the cachedself._session.<method>(...):_api_get,_api_post,_api_put,_upload_file,_send_url_as_file, the inline downloads insend_multiple_images, and the file-download path in_handle_ws_event.ClientTimeoutfromself._session = aiohttp.ClientSession()inconnect()— the cached session is now used only for the WebSocket, which usesheartbeat=for liveness.+124 / -79lines, no public-API changes, no new dependencies.Same architectural pattern as #1736 (
fix(gateway): SMS session-per-send, merged) and #3818 (fix(whatsapp): reuse persistent aiohttp session across requests, merged).How to Test
MATTERMOST_URLandMATTERMOST_TOKEN.hermes gateway start.send_messagetool. Example:WARNING agent.tool_executor: Tool send_message returned error … "Plugin platform send failed: Timeout context manager should be used inside a task"and the agent never delivers.{"success": true, "message_id": "..."}.Out-of-process and gateway-loop paths still work unchanged:
hermes send -t mattermost "test"(out-of-process) — unchanged.send_messagetool from agent contextPlugin platform send failed: Timeout context manager…(every call)_handle_ws_eventfile download (gateway loop)hermes send -t mattermost "…"out-of-processChecklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/gateway/test_mattermost.py -qand all tests passDocumentation & Housekeeping
cli-config.yaml.exampleif I added/changed config keys — N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/A