← 所有文章
指南

X Chat 私信失败排查:登录状态、密钥版本与消息签名

X 账号能够登录,并不意味着每个加密会话都已经可用。账号访问权限、正确的会话密钥和有效的消息签名,是不同的条件。排查应先确定失败发生在认证、加密消息准备、发送请求,还是消息进入业务系统的环节。仅凭“发送失败”,无法判定是签名错误、限流或账号受限。

先确认这四件事

  • 是一个会话失败,还是整个账号异常?问题出现在发送、接收,还是两者都有?
  • 对于加密消息,要检查对应的会话密钥版本,不能认为缓存里有密钥就足够。
  • 区分 X Chat 消息签名与 UnifyPort Webhook 验签。
  • 在重试或调整账号连接前,先保留请求标识和发生时间。

登录状态与聊天密钥各自负责什么

X 官方加密说明区分了身份密钥、签名密钥和带版本的会话密钥。可以据此建立排查模型:

材料职责应检查的问题
账号会话访问账号是否为预期账号,会话是否可用?
身份私钥解开分发给该用户的会话密钥是否具备匹配的身份密钥材料?
签名私钥为消息及支持的状态变更签名是否选用了正确的签名密钥和版本?
会话密钥加密或解密会话内容是否具备这条消息所需版本的密钥?

这些检查发生在不同层。使用托管连接器的应用开发者通常检查公开账号状态和错误响应,由连接器维护者继续排查协议密钥。API 认证通过,不能证明后面的检查也已通过。

用于恢复聊天密钥的 Chat 口令,也不同于 API key 或账号登录凭据。恢复失败时,应沿用账号所有者已有的 Chat 设置。不要把重置口令当作普通重试:X 的 Chat 帮助文档说明了口令不可用时恢复加密历史记录的限制。

改动配置前,先缩小失败范围

保留一次失败请求,并与已知正常的调用对比。例如,用户资料查询正常,但某个会话发送失败,应优先调查该会话,不能直接认定整个服务不可达。这样的对比只能缩小范围,还不能确定根因。

症状保留什么证据下一步
无法访问账号认证响应、账号及运行状态按当前授权文档处理账号访问问题。
只有一个会话发送失败请求 ID、会话 ID、准确的错误类别由连接器维护者检查会话状态、密钥版本、token 和签名输入。
部分消息无法解密受影响的消息 ID,以及可获得的密钥版本检查对应历史版本的密钥是否存在。
发送结果不确定请求时间、响应或超时、接收端结果先核对这次发送,再决定重发;超时可能留下未知结果。
X 收到消息,业务系统没收到Webhook 配置、投递尝试、接收端响应单独排查事件投递,不与 Chat 加密混在一起。

如果直接使用 X Chat SDK,X 官方排障指南覆盖了密钥初始化、会话缺钥、解密及签名失败。文中的 SDK 方法和错误消息不等同于 UnifyPort 的公开 API 契约。

缺钥之后,需要明确恢复和失败处理

对连接器实现而言,一条可采用的恢复路径是:

  1. 确认会话和事件要求的精确密钥版本。
  2. 查询该版本的缓存。
  3. 通过当前集成支持的恢复路径获取被封装的密钥材料。
  4. 校验材料,使用对应身份私钥解开,并保存到版本缓存。
  5. 重新读取缓存,在有界重试策略内恢复受影响的操作。

这是实现思路,不代表每个已连接账号都能恢复所有历史消息。恢复请求成功后,如果目标密钥仍不存在,就不能宣称恢复完成。同一密钥的并发请求可以共享恢复工作,但每条消息的处理结果仍要分别记录。旧密钥可以保留用于解密历史消息,同时不应覆盖较新的默认密钥。

收消息与发消息的失败处理也不同。接收侧应在支持的重试窗口内保留原始事件,不能把密文当作已解密消息。发送侧则需要明确:恢复失败是向调用方报错,还是允许现有回退路径。如果回退改变了加密属性,就不能把它描述为等价的加密投递。两条路径都不应无限重试。

签名问题需要签名层的证据

只有上游响应确实指出签名失败时,维护者才应针对性检查签名密钥选择、发送者身份、密钥版本以及参与签名的准确字节。重复提交相同的无效载荷,不会纠正这些输入;关闭验签也不能修复问题。

单个 HTTP 错误或宽泛的 Provider 错误,不能证明签名失败。同样,连接器中的签名修复只能证明其实现发生了调整,不能证明 X 修改了推荐算法,也不能单凭它断定 X 最近更新了协议。

通过 UnifyPort 公开 API 排查

UnifyPort 为已连接的消息账号提供非官方接口。先对照当前 X 授权指南,再检查 GET /v1/accounts/{account_id}/authGET /v1/accounts/{account_id}。认证状态与 runtime_status 用于判断连接状态,并不是逐会话的加密健康检查。

对于已有的 POST /v1/messages 请求,可以保留精简的诊断摘要。下面的函数接收调用方已经获得的 Response,不会发送或重试消息:

async function recordMessageAttempt(response) {
  const body = await response.clone().json().catch(() => null);
  console.info({
    observed_at: new Date().toISOString(),
    http_status: response.status,
    request_id: body?.request_id ?? response.headers.get('X-Request-Id'),
    code: body?.error?.code,
    numeric_code: body?.error?.numeric_code,
  });
}

消息账号 ID、相关会话和消息 ID 应保存在限制访问的排障记录中。公开 codenumeric_code 的定义以错误参考为准。宽泛的 provider_unavailable 不会直接揭示某个 X 签名错误,也不能自行把内部 X 错误与公开错误码建立一一对应关系。

这个函数刻意省略消息正文、cookies、会话 URL、PIN 和密钥材料。如果根本没有收到 HTTP 响应,应记录客户端失败、操作和时间,此时可能没有可用的服务端请求 ID。不要生成一个 ID 并把它作为服务端证据。

Webhook 签名保护的是另一段连接

签名验证对象排查位置
X Chat 消息签名被签名的 Chat 事件X Chat 客户端或连接器协议实现
X-Device-SignatureUnifyPort 发给接收端的投递请求Webhook 接收端及其 signing_secret

后者使用 HMAC-SHA256,对时间戳、一个句点和原始请求体进行计算,具体见 Webhook 投递参考。修复这层 HMAC 校验,并不能补齐 X 会话密钥;HMAC 验证成功,也不能证明一条出站消息已到达对方。

可按 Webhook 优先的接入检查清单核对接收配置。下游应用结构则可参考 X 私信与提及监听器实录。验证入站事件后先保存,再执行耗时的路由工作。

常见问题

重新登录一定能补齐密钥吗?

不能保证。更新会话并不证明所需身份密钥或会话密钥版本已经可用。应先定位缺失材料,再决定是否重新配置账号。

密钥恢复成功,就代表消息已送达吗?

不是。密钥可用、提交被接受、接收端收到消息、Webhook 接收服务处理完成,是不同的观察结果。需要验证业务真正关心的环节。

能直接调用 UnifyPort 的公开接口恢复 Chat 密钥吗?

本文没有引入这样的公开接口。请使用已记录的公开 API,并向支持团队提供诊断标识。内部连接器操作不能直接当作公开 API 路由使用。

能把所有 X 私信都称为加密消息吗?

不能。X 的 Chat 文档描述了未加密消息请求的场景。应先确认实际会话和发送路径,再说明其加密属性。

下一步

测试具体载荷前,先查看当前消息能力矩阵。如果直接对接 X 官方 Chat API,应使用其 SDK 和恢复文档;它的认证方式及事件契约与 UnifyPort 不同。

一手来源

核对日期:2026 年 9 月 10 日。

UnifyPort API

让消息接入变成一条稳定的产品管线。

先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。