错误处理
本页用于定位路由和中间件的失败。先确认 HTTP 状态码、响应体、请求的 Accept 和 requestId,再区分输入校验、业务拒绝、认证和未知异常。沿请求调用链抛出或 await 到的错误会交给框架处理;脱离调用链的后台 Promise 或定时器错误不能假定会变成当前请求的响应。
路由处理与校验的职责边界见 HTTP 与路由规范。
按症状定位
路由声明问题见 路由指南,白名单问题见 中间件指南,AUTH_* 含义见 auth 参考。
最小复现与验证
在可启动的 VextJS 项目中新建以下文件;尚未建项目时先完成 快速开始。该例不需要 service 或数据库;以默认 response.hideInternalErrors: true 及 JSON 请求验证。
在项目根目录创建两个 UTF-8 请求文件:error-invalid.json 内容为 {},error-valid.json 内容如下:
运行 npm run dev,在另一个终端发送下列请求。假设地址为 http://127.0.0.1:3000,统一设置 Accept: application/json;Windows PowerShell 使用 curl.exe 调用实际 curl。
逐条核对以下结果:
POST 同时设置 Content-Type: application/json。若结果不同,先对比项目的 response 配置、validator、错误 hook 和中间件,再比较业务代码;不要通过关闭校验或吞掉异常来让用例变绿。
app.throw
问题:预期业务拒绝却返回 500,或业务 code 丢失。 检查是否用了普通 new Error(),以及是否把业务码误当 HTTP status。需要指定状态、业务码或 details 时使用以下结构化入口;修复后分别检查 HTTP status 与响应体 code。
app.throw 适合“我要主动返回明确 HTTP 错误”的场景,例如 401、404、409、502,或需要业务码、i18n 参数、三方错误详情的响应。
app.throw() 返回类型是 never,调用后会中断当前处理流程,不需要额外 return。
details
问题:三方错误详情没有出现在响应中。 默认不会把三方错误对象的全部属性作为响应正文;检查是否明确提供了 details,以及清洗后是否还有可序列化内容。
details 用来显式返回业务详情,常见于三方接口或下游服务错误:
- 上游业务码、原始 message、trace id
- 可展示给调用方的失败原因
- 业务方自行裁剪后的三方响应片段
框架会在写出响应前做 JSON-safe 清洗:循环或重复对象引用会变成 "[Circular]",Date 输出 ISO 字符串,Error 只输出 name/message。对象中的函数和 undefined 属性会省略;在数组中则保留位置并替换为 null。深度达到 8 的对象会截断为 "[MaxDepth]",BigInt 会转为字符串。
顶层标量会包装为 { value: ... };空对象、空数组及无法保留任何值的 details 会省略。因此响应中的 details 不保证与传入对象完全同形。
推荐通过 HttpError 或 app.throw 明确传入 details。错误归一化也会读取异常上显式附加的 details 字段并清洗,因此不能把 hideInternalErrors 当作任意自定义详情的过滤器;不要把整个三方异常直接当成允许公开的详情。
响应格式
code 优先使用显式业务码,其次可能来自 i18n 配置;没有业务码时回退为 HTTP status。HTTP status 与 body.code 是两个字段。
与普通 Error 的区别
问题:只看到 500,或浏览器与 curl 返回格式不同。 普通 new Error() 默认进入 500;归一化也会读取错误上的 status / statusCode。使用明确的 HTTP 错误入口能避免依赖三方异常形状。
response.hideInternalErrors 默认 true,会隐藏未知 5xx 的内部消息和 stack;本地确需查看 JSON stack 时可以设置 false。显式结构化错误按自身合同输出。浏览器 Accept 包含 text/html 时,开发覆盖层或页面错误渲染可能返回 HTML;排查 JSON API 时明确请求 application/json。
服务端默认记录未知异常和 HttpError 5xx;校验错误不记录,HttpError 4xx 需 response.logErrors.http4xx: true 才记录。若日志缺失,检查 logger、logErrors 和错误是否被自定义中间件捕获后吞掉;只做日志上报的 catch 应重新抛出原错误。
校验错误
问题:相似输入有时返回 400、有时返回 422。 先看校验位置:路径参数使用 400,其他声明位置使用 422。不要只按“数据不合法”把两者合并。检查原始输入、schema 与 errors[].field,修复请求后重试。
路由 validate 失败时,非法路径参数返回 HTTP 400,query、header、cookie 与 body 失败返回 HTTP 422,两者都包含字段级错误详情。自定义字段级错误可以抛出 VextValidationError: