Skip to content

fix(dingtalk): deliver ERROR and INFO replies instead of dropping them - #3282

Merged
zhayujie merged 1 commit into
zhayujie:masterfrom
Lesereingrape:fix/dingtalk-reply-type-fallback
Sep 29, 2026
Merged

zhayujie merged 1 commit into
zhayujie:masterfrom
Lesereingrape:fix/dingtalk-reply-type-fallback

Conversation

@Lesereingrape

Copy link
Copy Markdown
Contributor

What does this PR do?

DingTalkChanel.send() dispatched on IMAGE_URL, FILE, VOICE and TEXT and had no branch for anything else, so an ERROR or INFO reply returned from send() without making a single API call. It now delivers those two types through the session webhook — the same helper the text path already uses when cards are off or the card failed — and logs the reply types this channel genuinely cannot render instead of returning in silence.

Type of change

  • Bug fix
  • New feature
  • Docs
  • Refactor / chore

Why it is a real defect

ChatChannel._send_reply (channel/chat_channel.py:318-356) ends with else: self._send(reply, context), and _decorate_reply keeps ERROR/INFO as their own types (it only prefixes the text). So every reply of those types arrives at send() and is dropped. The producers are not rare paths:

  • bridge/agent_bridge.py:935, :1590 and :1842 — Reply(ReplyType.ERROR, f"Agent error: {str(e)}"). When a turn raises, the user gets silence instead of the error.
  • Every model provider answers an unsupported inbound message with Reply(ReplyType.ERROR, ...) (models/chatgpt/chat_gpt_bot.py:185, :202, :284 and the equivalent lines in the other providers), and the voice providers report a failed synthesis the same way.
  • godcmd and the role plugin report command results as INFO (plugins/godcmd/godcmd.py:452, plugins/role/role.py:145, :177, :203, :208), so #help, #clear and role presets acknowledge nothing on DingTalk. models/chatgpt/chat_gpt_bot.py:131-137 does the same for "记忆已清除" / "配置已更新".

Every other channel already treats these three types as one text class — channel/telegram/telegram_channel.py:656, channel/wechatcom/wechatcomapp_channel.py:107, channel/discord/discord_channel.py:458, channel/slack/slack_channel.py:461, and feishu reaches the same result by leaving msg_type = "text" as the default (channel/feishu/feishu_channel.py:899) — and the ones that cannot render a type fall back with an explicit log: channel/qq/qq_channel.py:590, channel/wecom_bot/wecom_bot_channel.py:1003. DingTalk was the outlier.

What changed

Two branches appended to the dispatch, no existing branch edited:

  • elif reply.type in (ReplyType.ERROR, ReplyType.INFO): stringify, skip an empty payload, and send through _reply_markdown_or_text, which already falls back to plain text when the webhook rejects markdown. Group chats are covered because that helper takes the same incoming_message the text path uses.
  • a terminal else that logs Unsupported reply type: <type>, not sent. An in-memory IMAGE, a VIDEO_URL extracted out of an answer and the WeChat-only card types need an upload or payload this channel does not implement; today they disappear without a trace.

How I checked

New tests/test_dingtalk_reply_type_fallback.py (6 cases) drives the real send() with a stubbed webhook: an agent error and a plugin INFO reach the chat as text, a group chat is covered with cards enabled, a webhook that rejects markdown still delivers the error as plain text, an empty reply sends nothing, and an unsendable media type is logged.

Before the change, on pristine 4c00ca6d:

$ python -m pytest tests/test_dingtalk_reply_type_fallback.py -q
5 failed, 1 passed in 2.45s

After:

$ python -m pytest tests/test_dingtalk_reply_type_fallback.py -q
6 passed in 0.59s
  • Channel-wide regression run: python -m pytest tests/ -q -k "dingtalk or telegram or weixin or wechat or reply" → 132 passed.
  • python -m pytest tests/ -q on this branch → 36 failed, 2070 passed, 34 skipped, 55 subtests passed; on a pristine 4c00ca6d worktree on the same machine and venv → 36 failed, 2064 passed, 34 skipped, 55 subtests passed. The 36 node IDs are byte-identical on both sides (symlink-, zoneinfo- and POSIX-path-dependent tests that need this Linux CI image); the only difference is the six new passing tests.
  • CI's lint gate: ruff check channel/dingtalk/dingtalk_channel.py tests/test_dingtalk_reply_type_fallback.py --select F --ignore F403,F405 → All checks passed!

Checklist

  • I have read the Contributing Guide
  • I tested this change locally
  • Code comments and docs are in English
  • Linked related issue (if any): closes #

ChatChannel._send_reply hands every reply that is not TEXT, IMAGE_URL or
VOICE straight to send(), and this channel's dispatch only covered
IMAGE_URL, FILE, VOICE and TEXT. An ERROR from a failed agent turn or an
INFO from godcmd and the role plugins therefore returned without making a
single API call, so the user saw the bot go quiet.

Reply through the session webhook the way QQ and WecomBot already do, and
log the reply types this channel genuinely cannot render instead of
leaving no trace in the log.
@zhayujie
zhayujie merged commit 87312c3 into zhayujie:master Sep 29, 2026
3 checks passed
@zhayujie

Copy link
Copy Markdown
Owner

Merged, thanks for the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants