错误码
内核报告的每一次失败都带一个稳定的错误码。调用方分支判断用的是错误码;它旁边的那句话是给人读 的,任何东西都不该对它做匹配。
如何读懂一次失败
失败的回答带四样东西:
| 字段 | 是什么 | 拿它做什么 |
|---|---|---|
code | 下面某个错误码 | 用它做分支 |
message | 一个消息键,不是句子 | 按用户语言渲染它 |
retryable | 同一个调用能否原样再来一次 | 决定要不要给"重试" |
actions | 用户能做的事,按内核给的顺序 | 按那个顺序展示 |
message 不是一句话
message 是像 error.deviceOffline 这样的键,由界面渲染。直接显示这个字段的程序会显示出一个键; 而对它做匹配的程序,会在某次翻译改动时第一次坏掉。
权限与策略
这些在任何东西运行之前就被拒绝。调用被理解了,而答案是"不行"。
| 错误码 | 含义 |
|---|---|
capability_denied | 该调用所需的能力没有被授予这个插件 |
scheme_not_allowed | URL 协议不在允许集合内 |
host_not_allowed | 主机不在插件声明的允许列表内 |
ip_not_allowed | 地址落在网络能力拒绝的范围内 —— 回环、链路本地、私有 |
redirect_blocked | 重定向指向了允许列表之外 |
为什么地址范围是拒绝而不是警告
持有 network:fetch:* 的插件被授予的是"访问互联网"的能力,不是"探测它所在网络"的能力。这是两种不同 的权限,而只有其中一种被展示给了用户。
设备与链路
这些与另一端有关,且大多是暂时的。
| 错误码 | 含义 | 可重试 |
|---|---|---|
device_offline | 目标设备当前不可达 | 是 |
not_paired | 两台设备未配对 | 否 |
transport_not_open | 链路存在但不可用 —— 仍在连接,或正在收尾 | 是 |
timeout | 操作未在时限内完成 | 是 |
这一组里只有 not_paired 需要动作
它是这一组里唯一重试也修不好的,也是唯一对用户有下一步的:配对。其余的等一等就过去了。
消息与参数
这些与"发出去的东西"有关。
| 错误码 | 含义 |
|---|---|
invalid_argument | 参数未通过校验 |
payload_too_large | 载荷超过了内联消息的上限 |
response_too_large | 响应体超过了配置的上限 |
unsupported | 该动作在此平台或此宿主版本上不受支持 |
传输
| 错误码 | 含义 | 可重试 |
|---|---|---|
transfer_failed | 传输因完整性之外的原因失败 | 是 |
integrity_mismatch | 收到的字节与声明的摘要不符 | 是 |
完整性不符不是"静默损坏"
摘要在字节发出之前就已声明,所以不符意味着到达的东西不是承诺的那个。内核会丢弃那个半成品文件而 不是留着它,重试会把这个传输从头再来一遍。
插件与运行时
| 错误码 | 含义 | 可重试 |
|---|---|---|
plugin_not_installed | 目标设备没有安装这个插件 | 否 |
plugin_trapped | 插件陷入陷阱、耗尽燃料,或超过内存上限 | 否 |
plugin_timeout | 插件超过了它的时间预算 | 否 |
protocol_violation | 插件超出了它声明过的能力,或用了不属于它的命名空间 | 否 |
protocol_version_unsupported | 对端说的是不兼容的协议版本 | 否 |
rate_limited | 太短的时间窗内调用太多次 | 是 |
internal | 意料之外的事情 | 是 |
为什么 internal 是可重试的
它读起来像是"不该重试"的那个码,而这里故意相反。internal 是内核没有预料到的失败,而它涵盖的情 形 —— 锁多握了一瞬、任务跑到了没地方 —— 是暂时性的次数远多于永久性的。拒绝重试的调用方,会把一次打嗝 变成用户得手动绕过的故障。
internal 故意不带结构
如果调用方需要对一个意料之外的失败做分支,那它就不是意料之外的 —— 它有个名字,而那个名字应该出现在上 面的表里。给 internal 加一个临时字段,会把它变成一个含义私有的错误码。
应当告诉用户什么
一次失败有多少该到达用户,与它带哪个错误码是两个不同的问题。
| 可见性 | 何时 | 界面怎么做 |
|---|---|---|
| 静默 | 短暂的网络噪声、本机停机 | 什么都不做 —— 它自己会过去 |
| 降级 | 功能能用,但不完整 | 说出来,但不当成错误 |
| 可见 | 用户需要采取行动 | 展示它,连同建议的动作 |
静默是一个决定,不是遗漏
重连期间掉一个包,不是值得告诉用户的事。一个把收到的每个错误码都报出来的界面,会让用户学会忽略所有 错误码。