Stitch SDK错误处理完全指南:StitchError错误码清单与指数退避自动重试原理

原创2026-10-10 14:00:371,377 阅读

Stitch SDK错误处理完全指南:StitchError错误码清单与指数退避自动重试原理

Stitch SDK(@google/stitch-sdk)是一个能通过文本提示词生成 UI 屏幕、并程序化提取其 HTML 与截图的开发工具包。当调用 Stitch API 失败时,SDK 不会抛出五花八门的原生异常,而是统一抛出一个结构化的 StitchError,并内置"指数退避 + 全抖动"的自动重试机制。这篇指南面向新手,带你一次性搞清楚:9 个错误码分别是什么意思、哪些错误会自动重试、退避延迟是怎么算出来的。


1️⃣ 为什么所有错误都长一个样?

传统写法里,网络超时、404、限流、认证失败各自是不同的异常类型,你只能靠 catch 后字符串匹配来区分。Stitch SDK 的做法更干净:

无论底层失败来自 HTTP 状态码、MCP 工具错误文本还是网络中断,最终都会归一化为一个 StitchError。

StitchError 继承自标准 Error(可以 instanceof 判断),并携带一组"可决策"的字段:

字段 含义
code 错误码,如 RATE_LIMITED(9 选 1,见下文清单)
message 人类可读的错误描述
recoverable 布尔值:这个错误值不值得重试? 新手最需要看的就是它
status 来自 HTTP 响应时的状态码(如 429、404)
toolName 失败时正在调用的 MCP 工具名(如 get_screen)
retryAfter 服务端通过 Retry-After 请求头要求的等待毫秒数

定义见 errors.ts,其中 StitchErrorCode 就是一个穷举的枚举,StitchError.fromUnknown() 还能把任意"野生"异常兜底转成带 UNKNOWN_ERROR 码的规范错误。


2️⃣ StitchError 错误码清单:9 种情况一次看懂

错误码在 spec/errors.ts 中以 Zod 枚举形式定义,全量清单如下:

错误码 典型来源 recoverable 你会怎么办
AUTH_FAILED HTTP 401,API Key / Token 无效 ❌ 检查 STITCH_API_KEY 或 OAuth 配置
NOT_FOUND HTTP 404,项目或屏幕不存在 ❌ 核对资源 ID
PERMISSION_DENIED HTTP 403,Key 无权访问该项目 ❌ 确认 Key 的归属项目
RATE_LIMITED HTTP 429,触发限流 ✅ SDK 对只读操作已自动重试
SERVICE_UNAVAILABLE HTTP 502 / 503 / 504,服务端故障 ✅ 稍后再试,SDK 已自动退避
NETWORK_ERROR 连接被重置、超时、拒绝连接等 ✅ 检查网络,SDK 会重连并自动重试
VALIDATION_ERROR 参数不合法(如非法 baseUrl) ❌ 修正代码中的输入
CLIENT_CLOSED 客户端已 close() 后仍在使用 ❌ 新建一个 StitchToolClient 实例
UNKNOWN_ERROR 无法归类的兜底错误 ❌ 查看 message 详情

其中 recoverable 的判定逻辑只认三种:限流、服务不可用、网络故障(error-mapping.ts 中的 isRecoverable)。也就是说,SDK 用这个字段明确告诉你:"重试有意义的是这 3 类,其余的重试也是徒劳。"


3️⃣ 错误分类原理:HTTP 状态码永远优先

那么一个失败到底被判成哪个码?分类函数 classifyError 遵循两条规则:

规则一:有 HTTP 状态码时,状态码说了算。

429 → RATE_LIMITED;502/503/504 → SERVICE_UNAVAILABLE;404 → NOT_FOUND;403 → PERMISSION_DENIED;401 → AUTH_FAILED;其他 5xx(如 500)→ UNKNOWN_ERROR。哪怕响应体里写着 "rate limit",403 依然判为 PERMISSION_DENIED——状态码永远压过文本。

规则二:没有状态码时(MCP 工具只返回错误文本),按关键词匹配。

SDK 用了一组带单词边界的正则来识别文本,这是很讲究的工程设计:

  • \brate limit|\b429\b 能匹配 "rate limited / rate limiting",但不会误伤 "crate limit";
  • \b404\b 只认独立的 404,所以 "1404 items" 不会被误判成 404;
  • \bauthorized\b 式的边界确保 "author"、"authorize" 永远不会被错分成 AUTH_FAILED。

这些"防误判"细节都有对应测试覆盖,见 error-mapping.test.ts。

此外,MCP 传输层抛出的裸 HTTP 错误(比如网关直接返回 429)会先经过 normalizeTransportError 归一化:它按状态码分类、解析 Retry-After 头,补上 status 与 toolName 后再交给重试逻辑;fetch failed、ECONNRESET 等网络特征则被归为 NETWORK_ERROR。


4️⃣ 指数退避自动重试:只重试"读",绝不重试"写"

这是 SDK 最有价值的设计。自动重试藏在 callTool 的重试循环 中,先回答三个关键问题:

Q1:哪些工具会被自动重试? 只有幂等的只读操作——名字以 get_ / list_ 开头的工具(如 list_projects、get_screen)。生成类 / 修改类工具(generate_*、edit_*、create_*)永远不会自动重试:重试一次生成,等于白白多烧几分钟算力和配额,还可能在服务端留下孤儿屏幕(client.ts 中的设计注释)。测试也锁死了这一条:generate_screen_from_text 遇到 429 立即抛错,只调用 1 次(retry.test.ts)。

Q2:哪些错误码会触发重试? 仅 RATE_LIMITED、SERVICE_UNAVAILABLE、NETWORK_ERROR 三种。NOT_FOUND 重试一万次也还是 404,所以一次都不重试。

Q3:等待多久再试?—— 指数退避 + 全抖动公式

delay = min(maxMs, baseMs × 2^attempt) × random(0..1)

实现见 computeBackoffMs。以默认配置(baseMs=250、maxMs=4000)为例,理论上界是:

第几次重试 (attempt) 延迟上界
1 250ms
2 500ms
3 1000ms
4 2000ms
5 及以上 4000ms(被 maxMs 封顶)

每次实际等待还会再乘一个 0~1 的随机数(全抖动),避免大量客户端在同一毫秒集体重连、对服务端形成"重试风暴"。如果响应带有 Retry-After 头(支持秒数或 HTTP 日期,见 parseRetryAfter),SDK 取 max(退避值, retryAfter),尊重服务端的最低等待要求。

还有一个贴心的细节:一旦遇到 NETWORK_ERROR,SDK 会把内部连接标记为断开(client.ts),下一轮循环自动重连后再发请求——你无需自己处理 socket 重建。


5️⃣ 重试参数怎么配?

重试策略通过 StitchToolClient 的 retry 配置项控制,Schema 定义在 spec/client.ts:

参数 默认值 说明
attempts 3 总尝试次数(含首次调用),上限 10
baseMs 250 指数退避的基准延迟(毫秒)
maxMs 4000 单次退避延迟的上限(毫秒)
retry: false — 一键关闭自动重试

注意:attempts: 3 意味着"失败 1 次后最多再试 2 次",3 次都失败才把 StitchError 抛给你(retry.test.ts)。REST 直连路径 httpPost(用于批量上传等写操作)出于同样的"不重试写"原则,明确不做自动重试。


6️⃣ 实战:捕获并正确响应 StitchError

官方 README 给出的推荐写法(README.md Error Handling):

import { stitch, StitchError } from "@google/stitch-sdk";

try {
  const project = stitch.project("bad-id");
  await project.screens();
} catch (error) {
  if (error instanceof StitchError) {
    console.error(error.code);        // "NOT_FOUND"
    console.error(error.message);     // 人类可读描述
    console.error(error.recoverable); // false → 别重试,检查 ID
  }
}

给新手的三条落地建议:

  • 先判断 instanceof StitchError,再读 code 做分支,不要解析 message 文本;
  • recoverable === true 但 SDK 已经重试过仍失败时,再决定要不要做应用级重试(比如生成类工具被限流,只能等冷却后手动发起);
  • AUTH_FAILED 出现 401 时,优先检查环境变量 STITCH_API_KEY 是否已加载——SDK 对 baseUrl 等非法配置也会以 VALIDATION_ERROR 提前暴露,别和认证失败混淆。

更多行为细节(429 网关错误归一化、503 + Retry-After 等)全部有单元测试锁定,欢迎阅读 retry.test.ts 与 error-mapping.test.ts 作为"活文档"。


📌 小结

  • Stitch SDK 用统一 StitchError + 9 个穷举错误码,取代了对散乱异常的字符串猜测;
  • 分类遵循"HTTP 状态码优先、单词边界关键词兜底",杜绝 "1404" 误判 404 这类乌龙;
  • 自动重试只覆盖 get_* / list_* 只读操作,用 min(maxMs, baseMs×2^n)×rand 的指数退避 + 全抖动,并尊重 Retry-After;
  • 生成类操作永不自动重试——保护你的配额与数据一致性;
  • 代码与测试入口:spec/errors.ts、spec/error-mapping.ts、client.ts、spec/client.ts。
登录后查看全文
stitch-sdk