DEBUG.WIKI
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
RackNerd;$21.99/年 · 1GB KVM
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Ky 校验响应报错,堆栈却找不到业务调用位置

Ky 2.0.2 的 SchemaValidationError 可能只留下库内两层堆栈。保留 schema 失败本身,在业务边界补充不含敏感参数的上下文和 cause。

多个接口都报 SchemaValidationError: Response schema validation failed,错误平台却把它们归在一起。展开堆栈,只能看到 validateJsonWithSchema 和 processTicksAndRejections,看不到业务里哪次调用发起了请求。

Ky 2.0.2、Node 24.19.0 的这组现象,首先表示响应数据没有通过传给 .json(schema) 的校验。堆栈缺少调用位置是另一个问题,不能为了恢复堆栈而跳过 schema,或把请求标成成功。

错误跨过了异步返回边界

旧实现中,JSON 校验在 await 之后抛出异常,若中间函数直接 return 一个尚未完成的 Promise,相应调用帧可能已经退出。V8 重建异步堆栈时,无法沿这些位置恢复完整调用链。

增加 Error.stackTraceLimit 只能允许保留更多已有帧,不能把已丢失的业务帧重新接上。SchemaValidationError 的 issues 能帮助定位哪个字段不符,但不保证携带请求 URL。

正式 Ky 2.2.0 的 JSON 校验路径已有显式 await;总时限相关代码也与 2.0.2 不同。这能说明实现已经变化,不能单凭两行源码就承诺所有运行时、所有 totalTimeout 配置都会输出完整业务堆栈。

在业务入口保留操作名称和原异常

如果现有监控需要可靠地区分入口,可以在发请求的业务函数中捕获这个特定错误,再抛出带 cause 的错误。下面示例接收项目原有的 Standard Schema 对象,不改变校验规则:

import ky, { SchemaValidationError } from 'ky';

async function loadUser(userSchema, userUrl) {
  try {
    return await ky.get(userUrl).json(userSchema);
  } catch (error) {
    if (error instanceof SchemaValidationError) {
      throw new Error('读取用户资料时响应未通过校验', {
        cause: error,
      });
    }
    throw error;
  }
}

userUrl 接收应用原有、已经配置并校验的绝对 JSON 接口 URL;Node 环境不能直接拿没有基准地址的相对路径作为请求目标。userSchema 使用项目实际的 schema,认证、超时等配置仍沿用现有请求封装。这里的 return await 让函数在本地等待拒绝,catch 才能处理它。改成直接 return Promise,会让这个 try/catch 错过异步拒绝。

新的错误在业务位置创建,cause 保留原来的 SchemaValidationError 与 issues。它不会制造已经丢失的旧堆栈;监控平台还需要支持展开或序列化 cause,才能同时看见两层信息。

操作名称使用固定文案即可,不要直接拼接含 token、查询参数或个人信息的完整 URL,也不要把全部响应数据塞进 message。

不要交给 HTTP 错误钩子兜底

响应能被读到并解析成 JSON,仍可能不符合业务 schema。SchemaValidationError 不等同于 HTTPError;在当前正式实现中,它也不属于 Ky 的 HTTP 生命周期错误。仅在 beforeError 中添加日志,不保证会收到这一校验异常。

同样,不应把每次 schema 失败都自动重试。若服务器稳定返回错误字段结构,重试只会重复相同问题。应从 issues 核对是字段缺失、类型变化还是接口版本不匹配,再由相应契约决定修复方向。

验收时用两个不同的业务入口分别触发预期的 schema 失败,检查它们能否以操作名称区分、cause 是否保留原 issues,正常响应是否照常返回。同时覆盖项目实际使用的 totalTimeout 设置;仅在不设总时限的路径看见调用帧,不能替代另一条路径的结果。