Skip to content

getMetaItem 的 overlay 读用裸 catch 把「sys_metadata 不可达」吞成「该项不存在」—— GET /meta/:type/:name 在存储故障时回一个无 code 的 400「not found」 #5532

Description

@baozhoutao

做 #5462(mapDataError 的 unknown-object 启发式误报)时,在同一个真实 harness 上打出来的旁证。不在那单范围内(那单的文件面被明确限定为 packages/rest/src/rest-server.ts + packages/rest 测试,本条的根因在 packages/metadata-protocol 的产出方),按 Prime Directive #10 单独记在这里,unassigned。

现象

sys_metadata 整体不可用时,GET /api/v1/meta/object/acct 回:

400 {"error":"Metadata item object/acct not found"}

无 code,措辞是「这一项不存在」,真实情况是「元数据存储读不到」。和 #5462 是同一族的错误结论(可用性故障被讲成「你要的东西不存在」),但产出方不同、路由不同、状态码也不同:#5462 那条是启发式在 REST 层把它升格成 404,这条是产出方自己就已经把故障转写成了「不存在」,REST 层拿到的时候真相已经没了。

顺带的两个次生事实:

根因(file:line)

packages/metadata-protocol/src/protocol.ts:3149,getMetaItem 的 customization-overlay 读:

} catch {
    // DB not available — fall through to registry / MetadataService
}

裸 catch,无日志。注释自己就写着 "DB not available",然后照 miss 处理。驱动抛的 SQLITE_ERROR: no such table: sys_metadata 到此为止,item 保持 undefined,一路穿过 registry / MetadataService 兜底,最后由 getMetaItemCached(同文件 :5413)转成:

throw new Error(`Metadata item ${request.type}/${request.name} not found`);

—— 一个既无 status 也无 code 的 Error。

复现(in-process,已实跑)

真实 ObjectQL + 真实 ObjectStackProtocolImplementation,驱动每个方法都抛 SQLITE_ERROR: no such table: sys_metadata(即 #5464 / #5462 用的那套 harness)。在协议对象上挂一层 Proxy 记录 rejection:

ASYNC-THROW getMetaItemCached status=undefined code=undefined name=Error
            msg=Metadata item object/acct not found
GET item 400 {"error":"Metadata item object/acct not found"} | logs 1

驱动的原始报文在这一行之前就已经被丢掉了。

为什么这是缺陷而不是设计

ADR-0110 D3 已经为这件事立过规矩,原话在 MetadataManager.loadDiagnosed 的注释里:miss 与 outage 是两个不同的事实、安全含义相反,消费方不得把 outage 读成「作者没声明」。#5108 按这条把 DatabaseLoader 的复数读路径修掉了。本条是同一条规矩在单数读路径上、且在更上一层(metadata-protocol 的 overlay 读,不是 metadata 的 loader)的又一处未覆盖点 —— #5108 的标题限定语就是「在复数读路径上」,所以这里不是它的重复。

对客户端的实际后果:Studio / Setup 在元数据库故障期间会把每一个对象都显示成「不存在」,而不是「后端故障」,处置方向完全相反。

可选方向(不代裁决)

  • A:这层 catch 区分「读不到」和「没有这一行」—— 只有真正的 miss 才 fall through,存储异常照实抛(或包成带 status: 503 / code 的错误),让 REST 层的 fix(rest): a declared 5xx no longer ships its own message to the client (#5437) #5464 消毒 + 日志口接住。
  • B:保留 fall-through 但至少 logger.error 一行,并在最终 not found 上带 degraded 标记,让上层能分辨。
  • C:把 getMetaItemCached 的终末 not found 改成带 code/status 的结构化错误(无论如何都该做,否则它永远落 mapDataError 的 400 终末兜底并逐字上线内部措辞)—— 但只做 C 不解决根因,故障依然被讲成 miss。

A 与 Prime Directive #12 / ADR-0110 D3 一致;C 是无论选哪条都该顺手补的卫生问题。

未验证的部分

只测了 GET /api/v1/meta/:type/:name 一条路由。同一个裸 catch 之下还有 draft 读分支(readState === 'draft',:3150-3163)会在同样情况下抛 NO_DRAFT / 404 —— 即「存储故障」被讲成「没有草稿」,形状相同但没实测。getMetaItems(复数)在同样条件下的表现也没测。

关联

#5462(同一个 harness 上发现;REST 层的启发式一侧,已由 PR #5530 收口)、#5108(DatabaseLoader 复数读,已修)、ADR-0110 D3、#5437 / #5464(REST 5xx 消毒与日志口,方向 A 的接住方)、#4754(saveMetaItem 的静默 catch 家族,写侧对位)。


Generated by Claude Code

Activity

  1. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    分诊:pm:queue + domain:engine。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 5, 2026
  3. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    认领:PM 循环第 2 轮主批(engine-core 车道)
    会话:session_01V7WetGmnfoXNn8cLieKKmx
    分支:claude/issue-5532-getmeta-outage-vs-miss
    Worktree:objectstack-issue-5532
    域:domain:engine-core
    文件面:packages/metadata-protocol/src/protocol.ts(getMetaItem overlay 读 + getMetaItemCached 终末错误;同型的 draft 分支与 getMetaItems 复数读一并核验)、metadata-protocol 测试、.changeset/*.md。
    修法按分诊确认:方向 A(catch 内区分 miss 与 outage,ADR-0110 D3 / #5108 先例锚定)+ 顺手 C(终末 not found 结构化)。前提已对合并后 main 复核:裸 catch 注释现于 :3365(issue 引用的 3149 已漂移),:3081 另有一处同型复数读。#5596/#5627 今天两次动过本文件,行号以内容定位。


    Generated by Claude Code

  4. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    Contributor

    复核通过,ACCEPT(engine-core 车道 PM,第 2 轮主批):PR #5705,CI 23 项全绿,转 ready 入队。

    交付超出立单面且全部有据:同族裸 catch 实测 4 处(单数 / draft / preview / 复数)统一走新私有判别 rethrowUnlessMetadataStoreUnprovisioned(复用 #5108/#4867 的 isMissingTableError,未建表良性、其余 503+SERVICE_UNAVAILABLE 挂 cause);方向 C 落地(终末 miss → 404+RESOURCE_NOT_FOUND);零新增 error-code 词汇(标准目录自带),spec/rest/metadata 生产码零改动。公开认账一条派发词漂移:我预设的 wire 翻转「400→404」是 #5489 之前的旧形状,dev 实测纠正为 500→404,方向不变、值以实测为准 —— 后续读者以 PR 的表为准。双向反向验证红集分离(7/5 与 3/9,证明 404 是方向 C 自身贡献而非拆分副产品),objectql 侧两个钉死旧行为的 fixture 按「整条替换」处理并保留良性对照。

    顺带发现已核实:#5706(getEffectiveLock 故障时保护闸门 fail-open,被拒的写被放行且无 denied 审计)—— 可达缺陷,建议分诊优先定级,本车道将在 protocol.ts 下一个空位优先认领;#5707(三层视图展示失真,finding)。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions