Feishu group chat WebSocket event delivery fixes
Cluster 50656 · gateway. Data as of
Nothing open on GitHub. Fix merged in #61770 on 10 Jul 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #61770. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #61770. That is separate from the fix event. - Still open
- Nothing open on GitHub.
Multiple bug reports and fix attempts addressing Feishu/Lark group chat messages and @mentions not being received or routed through the WebSocket gateway. A minor unrelated gateway fix for file path rejection is also present.
Open issues0
Open PRs0
Fixes on record
-
#61770 fix(feishu): restore group events over WebSocket
files:
infographic/feishu-group-events/infographic.png,plugins/platforms/feishu/adapter.py,pyproject.toml,scripts/release.py,tests/gateway/test_feishu.py,tools/lazy_deps.pyShow the diff
-
plugins/platforms/feishu/adapter.py@@ -4730,6 +4730,12 @@ async def _connect_websocket(self) -> None: log_level=lark.LogLevel.INFO, event_handler=self._event_handler, domain=domain, + # Channel SDK signaling tag: without this UA tag the Feishu + # server does not push group @mention events over the WebSocket + # transport. The tag tells the server to use the Channel protocol + # which enables group-message routing in addition to P2P DM. + # See https://github.com/NousResearch/hermes-agent/issues/50656 + extra_ua_tags=["channel"], ) self._ws_future = loop.run_in_executor( None, -
pyproject.toml@@ -249,7 +249,7 @@ termux-all = [ "hermes-agent[web]", ] dingtalk = ["dingtalk-stream==0.24.3", "alibabacloud-dingtalk==2.2.42", "qrcode==7.4.2"] -feishu = ["lark-oapi==1.5.3", "qrcode==7.4.2"] +feishu = ["lark-oapi==1.6.8", "qrcode==7.4.2"] google = [ # Required by the google-workspace skill (Gmail, Calendar, Drive, Contacts, # Sheets, Docs). Declared here so packagers (Nix, Homebrew) ship them with -
scripts/release.py@@ -46,7 +46,7 @@ # Auto-extracted from noreply emails + manual overrides AUTHOR_MAP = { "[email protected]": "lemonwan", # PR #59430 sibling salvage (adapter reconnect contract guard) - "[redacted:email]": "luxuguang-leo", # PR #52966 salvage (QQBot reconnect contract) + "[redacted:email]": "luxuguang-leo", # PR #52966 + #52908 salvage (QQBot reconnect + Feishu Channel signaling) "[redacted:email]": "evelynburger", # PR #57544 salvage (gateway: webhook payload filters + route scripts; commit under unlinked identity) "[redacted:email]": "SiteupAgencia", # PR #57435 salvage (tui_gateway: back off notification poller when session is busy; #55578) "[email protected]": "rainbowgore", # PR #59405 salvage (mcp: bound stdio initialize handshake to stop subprocess/FD leak; #59349) -
tests/gateway/test_feishu.py@@ -173,6 +173,16 @@ def test_normalize_interactive_card_preserves_title_body_and_actions(self): class TestFeishuAdapterMessaging(unittest.TestCase): + @unittest.skipUnless(_HAS_LARK_OAPI, "lark-oapi not installed") + def test_websocket_sdk_accepts_channel_ua_tag(self): + """The shipped SDK must support the Channel signaling argument.""" + import inspect + + from lark_oapi.ws import Client as FeishuWSClient + + signature = inspect.signature(FeishuWSClient) + self.assertIn("extra_ua_tags", signature.parameters) + @patch.dict(os.environ, { "FEISHU_APP_ID": "cli_app", "FEISHU_APP_SECRET": "[redacted:secret-assignment]", @@ -414,6 +424,65 @@ def is_closed(self): self.assertEqual(sleeps, [1]) self.assertEqual(fake_loop.calls, 2) + @patch.dict(os.environ, { + "FEISHU_APP_ID": "cli_app", + "FEISHU_APP_SECRET": "[redacted:secret-assignment]", + }, clear=True) + def test_connect_websocket_sets_channel_ua_tag(self): + """Verify that FeishuWSClient receives extra_ua_tags=["channel"]. + + Without this UA tag the Feishu server does not push group @mention + events over the WebSocket transport. See + https://github.com/NousResearch/hermes-agent/issues/50656 + """ + from gateway.config import PlatformConfig + from plugins.platforms.feishu.adapter import FeishuAdapter + + adapter = FeishuAdapter(PlatformConfig()) + ws_client = SimpleNamespace() + + with ( + patch("plugins.platforms.feishu.adapter.FEISHU_AVAILABLE", True), + patch("plugins.platforms.feishu.adapter.FEISHU_WEBSOCKET_AVAILABLE", True), + patch("plugins.platforms.feishu.adapter.lark", + SimpleNamespace(LogLevel=SimpleNamespace(INFO="INFO", WARNING="WARNING"))), + patch("plugins.platforms.feishu.adapter.EventDispatcherHandler") as mock_handler_class, + patch("plugins.platforms.feishu.adapter.FeishuWSClient") as mock_ws_client, + patch("plugins.platforms.feishu.adapter._run_official_feishu_ws_client"), + patch("plugins.platforms.feishu.adapter.acquire_scoped_lock", return_value=(True, None)), + patch("plugins.platforms.feishu.adapter.release_scoped_lock"), + patch.object(adapter, "_hydrate_bot_identity", new=AsyncMock()), + patch.object(adapter, "_build_lark_client", return_value=SimpleNamespace()), + ): + _mock_event_dispatcher_builder(mock_handler_class) + + loop = asyncio.new_event_loop() + future = loop.create_future() + future.set_result(None) + + class _Loop: + def run_in_executor(self, *_args, **_kwargs): + return future + def is_closed(self): + return False + + try: + with patch("plugins.platforms.feishu.adapter.asyncio.get_running_loop", + return_value=_Loop()): + connected = asyncio.run(adapter.connect()) + finally: + loop.close() + + self.assertTrue(connected) + # Verify the Channel SDK UA tag is present — this is the fix for + # group @mention message delivery over WebSocket. + mock_ws_client.assert_called_once() + call_kwargs = mock_ws_client.call_args.kwargs + self.assertIn("extra_ua_tags", call_kwargs, + "FeishuWSClient must receive extra_ua_tags for group @mention delivery") + self.assertEqual(call_kwargs["extra_ua_tags"], ["channel"], + "extra_ua_tags must be ['channel'] to enable group event routing") + @patch.dict(os.environ, {}, clear=True) def test_edit_message_updates_existing_feishu_message(self): from gateway.config import PlatformConfig -
tools/lazy_deps.py@@ -188,7 +188,7 @@ "qrcode==7.4.2", ), "platform.feishu": ( - "lark-oapi==1.5.3", + "lark-oapi==1.6.8", "qrcode==7.4.2", ), # WeCom callback-mode adapter — parses untrusted XML POST bodies. Pulls -
uv.lock@@ -1794,7 +1794,7 @@ requires-dist = [ { name = "honcho-ai", marker = "extra == 'honcho'", specifier = "==2.0.1" }, { name = "httpx", extras = ["socks"], specifier = "==0.28.1" }, { name = "jinja2", specifier = "==3.1.6" }, - { name = "lark-oapi", marker = "extra == 'feishu'", specifier = "==1.5.3" }, + { name = "lark-oapi", marker = "extra == 'feishu'", specifier = "==1.6.8" }, { name = "markdown", specifier = "==3.10.2" }, { name = "mautrix", extras = ["encryption"], marker = "extra == 'matrix'", specifier = "==0.21.0" }, { name = "mcp", marker = "extra == 'computer-use'", specifier = "==1.26.0" }, @@ -2184,7 +2184,7 @@ wheels = [ [[package]] name = "lark-oapi" -version = "1.5.3" +version = "1.6.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -2194,7 +2194,7 @@ dependencies = [ { name = "websockets" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/ff/2ece5d735ebfa2af600a53176f2636ae47af2bf934e08effab64f0d1e047/lark_oapi-1.5.3-py3-none-any.whl", hash = "sha256:fda6b32bb38d21b6bdaae94979c600b94c7c521e985adade63a54e4b3e20cc36", size = 6993016, upload-time = "2026-01-27T08:21:49.307Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ad/1ab04db5d549ad1a7a2cd33682b1c38dee1d65019cb24fd7a23270e6337d/lark_oapi-1.6.8-py3-none-any.whl", hash = "sha256:9b443a5d47a7d204dd42dc40896c8b75087cc35788e45c48c140806d7df7e5e8", size = 7798300, upload-time = "2026-06-02T07:40:05.492Z" }, ] [[package]]
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#52908 fix(feishu): add extra_ua_tags=["channel"] to FeishuWSClient for group @mention delivery · merged via another change →
#61770
Closed issues · 1
- Closed#50656[Bug]: Feishu/Lark group @mention messages not delivered — WebSocket never receives group events
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- gateway
- Merged PRs
- #61770
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 10 Jul 2026