不能靠 error.cause 传递业务状态码构建跨服务溯源链路,因其仅限单运行时内存引用,json序列化、网络传输后丢失;状态码须显式注入api响应、opentelemetry标签、日志及前端上报等可传输载体。

不能靠 Error.cause 直接传递业务原始状态码来构建跨服务的深层溯源链路。它只在单个 JavaScript 运行时内有效,无法随 HTTP 响应、gRPC 消息或日志落盘自动携带状态码——JSON 序列化会丢弃 cause,网络传输后原始错误对象彻底消失。
为什么状态码不能“挂”在 cause 上透传
cause 是内存引用,不是可序列化字段:即使你写 throw new Error("下单失败", { cause: err }),且 err 自身带 code: "PAYMENT_TIMEOUT",一旦这个错误被 fetch 返回、JSON.stringify() 记录或跨进程传递,cause 就没了。浏览器控制台能看见,是因为没出 V8 上下文;Node.js console.error() 能展开,是运行时特殊支持,不是标准行为。
- HTTP 响应体里只有你显式塞进去的字段,比如
{ code: "ORDER_FAILED", cause: { code: "DB_CONN_REFUSED" } } - OpenTelemetry Span 中要记录状态码,得调用
span.setAttribute("error.code", err.cause?.code),而不是指望自动提取 - 前端收到 500 响应后,不能
throw new Error(res.data.message),而要解析res.data.cause并人工重建上下文
真正该把状态码放在哪儿
状态码必须作为结构化元数据,显式注入可传输、可存储、可检索的载体中:
- API 响应 payload:统一约定
error.cause.code、error.cause.service、error.cause.trace_id字段 - OpenTelemetry trace tags:网关层捕获异常后,提取
err.cause?.code写入error.cause.code标签,而非依赖 cause 对象本身 - 日志结构体:使用
pino或winston的child()机制,把code、service、trace_id作为上下文字段打点 - 前端错误上报:将原始响应中的
cause解析为扁平字段,如cause_code: "CACHE_UNAVAILABLE",再发给监控平台
如何在单进程内合理用 cause 辅助本地调试
在 Node.js ≥16.9 或 Chrome ≥100 环境中,cause 是极好的本地开发与单服务日志增强工具,但仅限于此:
- 封装
wrapError(message, originalErr)工具函数,强制检查originalErr.code并存入err.code(非 cause),确保关键码不丢失 - 日志系统打印时,主动展开 cause 链:
logErrorChain(err)递归输出err.code和err.cause?.code,避免只打err.stack - 自定义
Error.prototype.toJSON方法,让JSON.stringify(err)至少包含code和cause?.code字段(注意兼容性) - 对
err.cause做类型防护:访问前先判断err.cause instanceof Error,防止cause是字符串或 null 导致崩溃
分布式场景下的等效替代方案
要实现“从 UI 到 DB”的全链路状态码溯源,必须放弃依赖 cause,转而建设协议级元数据透传能力:
- 所有服务强制透传
traceparentheader,确保 Span 可关联 - 错误发生时,Span 必须设置
status.code = ERROR,并写入error.code、error.service、error.host等属性 - 网关统一拦截 5xx 响应,提取响应体中的
cause.code,注入到下游请求 header 或 trace tags 中 - 前端监控 SDK 在捕获未处理异常时,若发现是 fetch 失败,自动读取
response.data.cause并合并进错误事件字段











