渠道问题排查

适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-13 | 来源:crates/cheng-adapterscrates/cheng-api/src/middleware/channel_auth.rscrates/cheng-api/src/services/adapter_health.rscrates/cheng-core/src/domain/channel

渠道问题按消息卡在哪一环能干净地区分开,所以请先定位断点,而不是靠猜:

平台 → Webhook/工作者 → 鉴权 → 归一化 → 会话 → 工作流 → 回复 → 平台
 ①          ②            ③        ④        ⑤       ⑥       ⑦
卡在 现象
① 平台根本没投递 日志里什么都没有
② 传输 什么都没到,但渠道显示 active
③ 鉴权 返回 401
④ 归一化 有投递日志,但没有消息落库
⑤ 会话 消息落库了,但没有运行
⑥ 工作流 运行起来了但失败
⑦ 回复 运行成功,但什么也没发回去

连接状态

unconfigured → configuring → active
                                │
        健康检查失败 → degraded → (自动重连)→ active
                                │
     disconnected(手动)    error { message }(需人工介入)
状态 含义
unconfigured 还没有凭证或配置
configuring 建立中——正在注册 Webhook、等待 OAuth 跳转或扫码
active 已连接、可工作
degraded 健康检查失败或令牌过期;正在自动恢复
disconnected 手动断开或已吊销
error 不可恢复;需要人工介入

卡在 configuring 的渠道几乎总是因为多步流程没走完——OAuth 跳转没跟进,或者二维码没扫。去看 setup_data

什么都收不到

首先:它是不是依赖工作者的平台? Telegram 轮询、Slack Socket Mode 和钉钉 Stream 都依赖后台工作者。健康服务把管理器的 is_running() 当作权威存活信号,正是因为凭证有效不等于消息在进来——工作者可能已经退出了。

工作者一旦死掉,渠道会在一个健康周期内(ADAPTER_HEALTH_INTERVAL_SECS,默认 300 秒)变为 degraded。恢复流程会调用管理器的 restart()——只调 connect() 仅仅校验凭证,会造成一种假恢复:数据库写着 active,实际什么都收不到。看到「active 但一片寂静」,就该怀疑这一条。

对于 Webhook 类平台(WhatsApp、企业微信、Webhook 模式的 Slack):

检查 原因
PUBLIC_BASE_URL 是你真实的公网 HTTPS 域名 Webhook URL 是运行时由它计算出来的
该 URL 公网可达且不是 localhost Telegram 的 Webhook 模式会校验这两点,并带明确提示让连接失败
平台控制台里显示 Webhook 已注册 有些平台会默默丢弃失败的注册
验证挑战通过了 WhatsApp 发一次性的 GET 带 hub.* 参数;Slack POST url_verification

在 NAT 后面或笔记本上跑? 选一种不需要入站连接的传输:Telegram 轮询(默认)、Slack Socket Mode 或钉钉 Stream。见即时通讯渠道

鉴权失败

渠道密钥的形式是 chk_{credential_id_hex}_{secret_hex}——chk_ 前缀、32 位十六进制的凭证 id,以及 32 位十六进制的密钥。它们作为 provider = "channel_adapter" 的凭证存储。

错误码,全部返回 401

错误码 含义
MISSING_TOKEN 没有 Authorization 请求头
INVALID_KEY_FORMAT 前缀不对、长度不对,或凭证 id 格式错误
KEY_NOT_FOUND 该凭证 id 不存在
WRONG_PROVIDER 凭证存在,但不是 channel_adapter 类凭证
SECRET_MISMATCH 密钥那一半校验不通过
INVALID_KEY 通用的密钥拒绝

除此之外都是 500——那是服务端问题,不是你的密钥问题。

密钥还可能是受限的:限定到某个 app_id(平台)以及某个精确的 channel_id。一把在某个渠道能用、在另一个渠道 401 的密钥,是受限,不是坏了。

浏览器前端绝不能使用 chk_ 密钥。 它们只用 JWT;适配器密钥属于服务端的平台服务。见渠道路由

另外,/webhooks/:channel_uuid 接口是刻意不带认证的——真实性由适配器的签名校验建立,而不是 Bearer 令牌。那里出现 401 说明是别的地方出了问题。

签名与挑战失败

各平台的校验方式不同,Normalization 或鉴权错误通常指向配错了密钥:

平台 机制 常见原因
Telegram X-Telegram-Bot-Api-Secret-Token 相等比较 配置里的 webhook_secret 与注册时不一致
WhatsApp X-Hub-Signature-256 上的 HMAC-SHA256 signing_secret 填错了(应是 app secret,不是 access token)
Slack 带时间戳的 X-Slack-Signature HMAC-SHA256 signing_secret 错,或主机时钟偏移
企业微信 SHA1 签名 + AES-CBC 加密 XML tokenencoding_aes_key 错(后者必须是 43 位 base64url)

消息到了,却没有运行

检查 说明
有没有绑定工作流? 渠道记录上的 bound_workflow_id工作流绝不从 channelId 推断
渠道是否 enabled
这条消息是可执行的吗? 空文本消息会被静默跳过;只含投递状态的更新会归一化成空数组
它是不是一条命令? ~ 开头的消息是渠道命令,会被消费而不是转发
工作流本身合法吗? 执行失败排查

重复与上下文丢失

重复回复通常意味着去重没生效。入站消息带有 external_message_id 正是为此;只要该 id 稳定,平台重复投递就是正常且无害的。

会话解析在 (workspace_id, app_id, external_chat_id) 上是幂等的,因此反复调用会返回同一个会话。如果某个用户总是落进新会话,说明 external_chat_id 不稳定——去看适配器在群聊与单聊时分别往那里放了什么。

会话轮换~new 或切换工作流)会在创建新会话的同一个事务里,把旧会话的 external_chat_id 加上 __retired_<ts> 后缀使其退役。在数据库里看到 retired 后缀是正常的,不是数据损坏。

回复没有送达

出站命令是 send_textsend_mediareplyreacttyping 之一。检查适配器声明的能力:Telegram 支持「正在输入」但不支持表情回应和编辑;Slack 支持回应、编辑和删除,但没有「正在输入」。执行不支持的动作会返回 NotSupported

错误类别

适配器错误是稳定的语义类别,类别会告诉你该去哪儿看:

类别 去看
Config connection_config 缺字段或填错
Auth 令牌无效、凭证过期、权限被拒
PlatformApi 平台返回了错误——常见于配额或应用被吊销
Normalization 载荷结构出乎意料;查已存消息里的 raw
NotSupported 该适配器不提供这个操作
Internal ChengOS 的 bug——带上轨迹提报

诊断清单

1. GET /workspaces/:ws/channels/:id/status       → 连接状态
2. GET /workspaces/:ws/channels/:id/capabilities → 该动作到底支不支持
3. 确认自上次变更以来已经过了一个健康检查周期
4. 工作者类平台:确认工作者在跑,而不只是凭证有效
5. 检查 bound_workflow_id
6. 查会话里有没有落库的消息 → 区分 ④ 和 ⑤
7. 查执行轨迹 → 区分 ⑥ 和 ⑦

下一步

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容