Skip to content

错误码对照

VerifyError 的每个变体都对应一个具体的界面动作。这是结构化错误的全部意义 —— Err(String) 只能让你显示一行红字,用户读完仍然不知道下一步该做什么。

速查表

codeRust 变体触发界面应当给的动作
auth_failedAuthFailed { detail }401 / 403把错误挂到密钥输入框上;若该预置有 apply_url,给「去申请密钥」
not_foundNotFound { requested_url, suggested_url }404suggested_url 非空 → 「一键改用」按钮;为空 → 显示实际请求的地址
unreachableUnreachable { proxy_hint }超时 / DNS / TLS「重试」proxy_hint 为真时提示去配代理
model_not_foundModelNotFound { available }端点不认这个模型available 展开成下拉让用户改选
protocol_mismatchProtocolMismatch { expect }密钥只接受另一种协议提示切到对应的服务商模板
missing_extra_fieldMissingExtraField { key }专有字段没填定位到那个输入框并标红
malformedMalformed { detail }端点返回了预期外内容兜底:显示摘要

唯一「改配置没用」的那个

rust
if e.is_actionable() {
    // 改配置能解决 —— 光标定位到对应输入框
} else {
    // 只有 Unreachable 属于这类 —— 给「重试」
}

网络不通时让用户去改配置是误导 —— 他会开始怀疑地址、怀疑密钥, 而实际上什么都没错。这个区分值得单独一个方法。

逐个说明

auth_failed

json
{ "code": "auth_failed", "detail": "Invalid API key provided" }

detail 是端点返回的原始说明摘要(截断 300 字符)。不含密钥 —— 可以安全地写进日志。

界面上把它挂到密钥输入框,而不是弹一个全局提示 —— 用户需要知道是哪个字段有问题。

probe 里 401 反而是成功信号

本库的 cargo xtask probe 不带密钥去探各家端点,此时 401/403 恰恰说明地址是对的,404 才是真问题。这个思路在你写健康检查时也用得上。

not_found

json
{
  "code": "not_found",
  "requested_url": "https://api.deepseek.com/models",
  "suggested_url": "https://api.deepseek.com/v1"
}

requested_url 直接展示给用户 —— 省去他猜"到底打了哪个地址"。 这是排查地址问题时最有用的一条信息,而多数客户端不给。

suggested_url 非空时给「一键改用」按钮。它只在确实看不到版本段时才有值:

base_urlsuggested_url
https://api.deepseek.comhttps://api.deepseek.com/v1
https://api.deepseek.com/v1null(已有版本段)
https://open.bigmodel.cn/api/paas/v4null(智谱是 v4)
https://generativelanguage.googleapis.com/v1beta/openainull

已经有版本段却 404,说明是别的问题 —— 这时候给建议会把用户引向另一个错误答案。

unreachable

json
{ "code": "unreachable", "proxy_hint": true }

proxy_hint 为真表示这个 host 在国内通常需要代理 (api.openai.comapi.anthropic.comgenerativelanguage.googleapis.comopenrouter.aiapi.groq.comapi.x.ai)。

此时提示「国内访问该站点通常需要代理」比单说「连接失败」有用得多 —— 后者会让用户去反复检查自己的密钥。

model_not_found

json
{ "code": "model_not_found", "available": ["deepseek-flash", "deepseek-v4-pro"] }

available 是端点返回的真实清单。正确做法是直接填进模型下拉:

typescript
case "model_not_found":
  setModelOptions(err.available);
  message.warning("端点不认识该模型,已为你载入可用清单");

让用户自己去翻文档找模型名是最差的处理 —— 清单明明就在手上。

protocol_mismatch

json
{ "code": "protocol_mismatch", "expect": "anthropic" }

典型场景:中转站的密钥限定 /v1/messages,而用户选了 OpenAI 兼容的模板。 提示他切到「Claude Code 客户端」那一档即可。

missing_extra_field

json
{ "code": "missing_extra_field", "key": "appid" }

这个错误最好永远不要出现

check_required_fields发起验证之前就判出来,直接把按钮置灰。

禁用态优于「点了才报错」—— 用户不会浪费一次往返,也不会怀疑是网络问题。 这个变体是第二道闸门(前端可能被绕过,也可能有别的调用方)。

malformed

兜底变体。端点返回了非 JSON、或结构完全对不上。

detail 不含响应体原文 —— 避免把可能夹带密钥的内容带进日志。

前端分支的注意事项

typescript
switch (err.code) {
  // …各分支
  default:
    // 🔴 必须有
    message.error("验证失败,请检查配置");
}

default 分支不能省

VerifyError#[non_exhaustive] —— 本库新增错误变体只算 minor 版本。 没有 default 的话,新变体会让界面什么都不显示。

「点了没反应」是最难排查的一类故障,用户也最容易认为是软件坏了。

同理,Rust 侧的 match 也必须有 _ 分支。

与 ParseError 的分工

回答的问题阶段
ParseError这段文本是不是一条配置粘贴导入时
VerifyError这条配置对不对点测试连接时

界面表现应当不同:前者是"你粘错东西了",后者是"配置本身有问题"。 详见 ai.profile 协议

相关

MIT 协议开源 · 文档同样欢迎 PR