Skip to content

mapDataError 的显式状态直通只覆盖 4xx,数据路由上一个声明了 502/503 的生产者拿不回自己的状态码(与 resolveErrorResponse 不对等) #5582

Description

@baozhoutao

发现于 #5489 的兜底测绘,不在该单文件面内,按 Prime Directive #10 单独立项。观察类:我是用注入错误测到的,没有找到会走到这条组合的活体生产者,严重度请分诊时判定。

事实

packages/rest/src/rest-server.ts 有两道错误门,对「生产者声明了 status」这件事给的判断不一样:

而 CRUD 数据路由是直接调 mapDataError 的(const mapped = mapDataError(error, req.params?.object),rest-server.ts 约 4804 / 4839 / 4894 / 4956 / 5034 / 5076,另有 6049 / 6462 / 6612 三处),不经过 resolveErrorResponse。所以同一个声明了 status: 502 的错误:

经 sendError / handleRouteError → 502 {"error":"Internal server error", ...}
直接经 mapDataError            → 声明的 502 丢失

实测(#5489 的兜底测绘,给终局兜底加桩跑 @objectstack/rest 全套):

{"raw":"connect ECONNREFUSED 10.0.0.5:5432 (internal pool)","name":"Error","status":502}

这个错误没有命中任何文本启发式,一路落到终局兜底。#5489 之前它以 400 携带主机与端口原文下发;#5489 之后它是 500 INTERNAL_ERROR(已消毒、在正确的段位)。残留的只有状态码保真度:声明的 502(「上游依赖不可达,可重试、可换节点」)被压成 500。

为什么值得记一笔

  • 502/503 与 500 对调用方不是同义词:isExpectedDataStatus 把 502/503 当正常生命周期结果(不打 unhandled 日志),500 不是;代理与重试策略也区别对待。
  • 这是同一个问题的两个答案,而 sendError 的显式状态直通覆盖 400–599,5xx 的原始驱动报错绕过全部泄漏启发式直达客户端(metadata-protocol 有活体产出方) #5437 的分析已经把「两道门对一个问题给相反判断」记成过缺陷形状本身。
  • 目前是观察类:仓内声明 5xx 且能走到数据路由直调点的生产者,我没有测到活体(ERR_DATASOURCE_UNAVAILABLE 有自己的 503 分支;metadata-protocol 的两个 overlay 500 走的是 meta 路由,即 resolveErrorResponse 那道门)。所以今天没有用户会撞上;但它是一条 mapDataError 被直调时就生效的静默降级。

可能的修法(供分诊)

mapDataError 的直通改为与 resolveErrorResponse 同款:4xx 截断措辞、5xx 保状态丢措辞留 code。注意 rest.test.tsdoes NOT pass through an explicit 5xx statusrest-4xx-message-truncation.test.ts5xx never enters this branch at all 两条 pin 是按现状写的,改动需一并重写(#5489 已把这两条从纯否定断言升级为钉住实际落点,便于下一手对照)。

相关:#5489(本单发现来源,已把终局兜底改为消毒 5xx)、#5437 / PR #5464#5462 / PR #5530#5532(同一片领地的另一半:getMetaItem 的裸 catch 把存储故障吞成 not found)。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions