Bug Description
The DingTalk adapter fails to receive messages with newer versions of dingtalk-stream SDK (>=
0.20).
Root Cause
dingtalk-stream changed two methods from sync to async:
ChatbotHandler.process() — was def, now async def. Returns a tuple (STATUS_OK, "OK") which
the SDK tries to await, causing "object tuple can't be used in 'await' expression".
DingTalkStreamClient.start() — was sync (blocking), now async def. The adapter wraps it
with asyncio.to_thread() which doesn't work for coroutines, causing "coroutine
'DingTalkStreamClient.start' was never awaited".
process() receives CallbackMessage, not ChatbotMessage — The message data (text,
senderId, sessionWebhook, etc.) is inside message.data dict, not as direct attributes.
_extract_text() gets nothing and logs "Empty message, skipping".
Steps to Reproduce
pip install "dingtalk-stream>=0.20"
hermes gateway run # with dingtalk configured
Send a message in DingTalk → no response
Error Logs
ERROR dingtalk_stream.client: error processing message: object tuple can't be used in 'await'
expression
RuntimeWarning: coroutine 'DingTalkStreamClient.start' was never awaited
ERROR dingtalk_stream.client: [start] network exception, error=
Suggested Fix
_IncomingHandler.process() — make async, parse CallbackMessage.data:
async def process(self, message):
data = message.data
if isinstance(data, str):
data = json.loads(data)
msg = ChatbotMessage()
msg.message_id = data.get('msgId', '')
msg.text = data.get('text', {})
msg.sender_id = data.get('senderId', '')
msg.sender_nick = data.get('senderNick', '')
# ... map all fields from data dict
await self._adapter._on_message(msg)
return dingtalk_stream.AckMessage.STATUS_OK, "OK"
_run_stream() — start() is async but needs its own event loop (it runs a blocking websocket
loop internally). Running it in the gateway's event loop causes [start] network exception,
error=. Needs to run in a separate thread with its own event loop, with a thread-safe queue to
pass messages back.
Environment
- Hermes Agent v0.9.0
- dingtalk-stream 0.24.3
- Python 3.12
Steps to Reproduce
- pip install "dingtalk-stream>=0.20"
- Configure DingTalk in config.yaml:
platforms:
dingtalk:
enabled: true
Set DINGTALK_CLIENT_ID and DINGTALK_CLIENT_SECRET in .env
- hermes gateway run
- Send any message to the bot in DingTalk
- No response. Errors in log:
- "coroutine 'DingTalkStreamClient.start' was never awaited"
- "error processing message: object tuple can't be used in 'await' expression"
- "[start] network exception, error="
Expected Behavior
dingtalk success
Actual Behavior
ERROR dingtalk_stream.client: error processing message: object tuple can't be used in 'await'
expression
RuntimeWarning: coroutine 'DingTalkStreamClient.start' was never awaited
ERROR dingtalk_stream.client: [start] network exception, error=
Affected Component
Agent Core (conversation loop, context compression, memory)
Messaging Platform (if gateway-related)
Telegram, N/A (CLI only)
Debug Report
ERROR dingtalk_stream.client: error processing message: object tuple can't be used in 'await'
expression
RuntimeWarning: coroutine 'DingTalkStreamClient.start' was never awaited
ERROR dingtalk_stream.client: [start] network exception, error=
Operating System
centos 8
Python Version
3.12
Hermes Version
No response
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
_IncomingHandler.process() — make async, parse CallbackMessage.data:
async def process(self, message):
data = message.data
if isinstance(data, str):
data = json.loads(data)
msg = ChatbotMessage()
msg.message_id = data.get('msgId', '')
msg.text = data.get('text', {})
msg.sender_id = data.get('senderId', '')
msg.sender_nick = data.get('senderNick', '')
# ... map all fields from data dict
await self._adapter._on_message(msg)
return dingtalk_stream.AckMessage.STATUS_OK, "OK"
_run_stream() — start() is async but needs its own event loop (it runs a blocking websocket
loop internally). Running it in the gateway's event loop causes [start] network exception,
error=. Needs to run in a separate thread with its own event loop, with a thread-safe queue to
pass messages back.
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?
Bug Description
The DingTalk adapter fails to receive messages with newer versions of dingtalk-stream SDK (>=
0.20).
Root Cause
dingtalk-stream changed two methods from sync to async:
ChatbotHandler.process()— was def, now async def. Returns a tuple (STATUS_OK, "OK") whichthe SDK tries to await, causing "object tuple can't be used in 'await' expression".
DingTalkStreamClient.start()— was sync (blocking), now async def. The adapter wraps itwith asyncio.to_thread() which doesn't work for coroutines, causing "coroutine
'DingTalkStreamClient.start' was never awaited".
process()receivesCallbackMessage, notChatbotMessage— The message data (text,senderId, sessionWebhook, etc.) is inside message.data dict, not as direct attributes.
_extract_text() gets nothing and logs "Empty message, skipping".
Steps to Reproduce
pip install "dingtalk-stream>=0.20"
hermes gateway run # with dingtalk configured
Send a message in DingTalk → no response
Error Logs
ERROR dingtalk_stream.client: error processing message: object tuple can't be used in 'await'
expression
RuntimeWarning: coroutine 'DingTalkStreamClient.start' was never awaited
ERROR dingtalk_stream.client: [start] network exception, error=
Suggested Fix
_IncomingHandler.process()— make async, parse CallbackMessage.data:async def process(self, message):
data = message.data
if isinstance(data, str):
data = json.loads(data)
msg = ChatbotMessage()
msg.message_id = data.get('msgId', '')
msg.text = data.get('text', {})
msg.sender_id = data.get('senderId', '')
msg.sender_nick = data.get('senderNick', '')
# ... map all fields from data dict
await self._adapter._on_message(msg)
return dingtalk_stream.AckMessage.STATUS_OK, "OK"
_run_stream()— start() is async but needs its own event loop (it runs a blocking websocketloop internally). Running it in the gateway's event loop causes [start] network exception,
error=. Needs to run in a separate thread with its own event loop, with a thread-safe queue to
pass messages back.
Environment
Steps to Reproduce
platforms:
dingtalk:
enabled: true
Set DINGTALK_CLIENT_ID and DINGTALK_CLIENT_SECRET in .env
Expected Behavior
dingtalk success
Actual Behavior
ERROR dingtalk_stream.client: error processing message: object tuple can't be used in 'await'
expression
RuntimeWarning: coroutine 'DingTalkStreamClient.start' was never awaited
ERROR dingtalk_stream.client: [start] network exception, error=
Affected Component
Agent Core (conversation loop, context compression, memory)
Messaging Platform (if gateway-related)
Telegram, N/A (CLI only)
Debug Report
Operating System
centos 8
Python Version
3.12
Hermes Version
No response
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
_IncomingHandler.process()— make async, parse CallbackMessage.data:async def process(self, message):
data = message.data
if isinstance(data, str):
data = json.loads(data)
msg = ChatbotMessage()
msg.message_id = data.get('msgId', '')
msg.text = data.get('text', {})
msg.sender_id = data.get('senderId', '')
msg.sender_nick = data.get('senderNick', '')
# ... map all fields from data dict
await self._adapter._on_message(msg)
return dingtalk_stream.AckMessage.STATUS_OK, "OK"
_run_stream()— start() is async but needs its own event loop (it runs a blocking websocketloop internally). Running it in the gateway's event loop causes [start] network exception,
error=. Needs to run in a separate thread with its own event loop, with a thread-safe queue to
pass messages back.
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?