Skip to content

sys_metadata 不可用被 mapDataError 的 unknown-object 启发式误报成 404 OBJECT_NOT_FOUND,且 404 属「预期状态」因而一行日志都不留 #5462

Description

@baozhoutao

做 #5437(sendError 5xx 直通消毒)时用真实 harness 打出来的旁证。不在那单范围内(那单只动 resolveErrorResponse 的显式 5xx 分支,⛔ 不动启发式本身),按 Prime Directive #10 单独记在这里,unassigned。

现象

驱动层 sys_metadata 整体不可用(表缺失/库不可达)时,一条不带 status 的原始驱动错误走到 mapDataError,被 looksLikeUnknownObject 命中 no such table,于是返回:

404 {"error":"Object not found","code":"OBJECT_NOT_FOUND"}

客户端读到的是「你要的对象不存在」,真实情况是「元数据存储整体挂了」。这两件事的处置完全相反:前者让人去查对象名拼写,后者该去看运维。

更要紧的是日志侧:isExpectedDataStatus 把 404 列为正常客户端错误,handleRouteError 因此不打 [REST] Unhandled error。所以这条基础设施故障在服务端一行都不留。

复现(in-process,已实跑)

真实 ObjectQL + 真实 ObjectStackProtocolImplementation,驱动的每个方法都抛 SQLITE_ERROR: no such table: sys_metadata:

PUT /api/v1/meta/object/acct
  => 404 {"error":"Object not found","code":"OBJECT_NOT_FOUND"}

Postgres 措辞(relation "sys_metadata" does not exist)同样命中 looksLikeUnknownObject 的第二条分支,结果一致。

成因

packages/rest/src/rest-server.ts 的 looksLikeUnknownObject 只看消息文本里有没有 no such table / relation ... does not exist,不区分缺的是哪张表。业务对象的表缺失和 sys_metadata 缺失是同一条正则的两个命中,但语义相反:前者确实约等于「对象没注册」,后者是元数据平面自身故障。

注意 #5437 只把显式声明了 5xx 的错误挡在启发式之前;这条路径上的错误不带 status,因此仍然整条走 mapDataError,不受 #5437 影响。

可选方向(不代裁决)

未验证的部分

只打了 PUT/DELETE /api/v1/meta/:type/:name 两条路由;数据路由(/api/v1/data/*)在 sys_metadata 挂掉时的表现没测。是否有别的系统表(sys_environment 等)落进同一条启发式也没清点。

关联

#5437(发现于此;只覆盖显式 5xx 一侧)、#3867(消毒器)、#4886(isExpectedDataStatus / 日志判定)、#3770(OBJECT_NOT_FOUND 单一信封)。


Generated by Claude Code

Activity

  1. baozhoutao commented on Aug 5, 2026

    @baozhoutao
    ContributorAuthor

    分诊(cli 车道 PM,session_016FNvXhtSdnEGEfLEsMmvxh,2026-08-05):入队 pm:queue,domain:cli。判级:真缺陷 —— 基础设施故障(sys_metadata 整体不可用)被 looksLikeUnknownObject 误分类成 404「对象不存在」,客户端读到错误结论,且 404 属 isExpectedDataStatus 连一行日志都不留 —— 双重静默,#5437 家族的启发式误判面(该单刻意不动启发式,本单正是收口处)。已在 #5464 的 harness 上实证复现。

    方向待派发时细化(启发式分类修正需读全部关键词分支再定,不预裁);基本约束先记:① 不带显式 status 的原始驱动错误不应被文本启发式升格为「预期 4xx」—— 误判方向宁可落 500 消毒(响亮 + 有日志)不可落 404 静默;② 修正后 #5464 的全部 pin 不得回归。

    ⛔ 串行约束(step 3 记录):与 PR #5464(#5437)同文件 rest-server.ts,须待其 MERGED 后基于新 main 派发,复用其 harness 与 logWithheldServerFault。另 #5448(serve.ts)与本单不同文件可并行,#5447 在飞。


    Generated by Claude Code

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

    @baozhoutao
    ContributorAuthor

    认领:PM 循环第 7 轮(cli 车道)
    会话:session_016FNvXhtSdnEGEfLEsMmvxh
    分支:claude/issue-5462-unknown-object-heuristic
    Worktree:objectstack-issue-5462
    域:domain:cli
    文件面:packages/rest/src/rest-server.ts(mapDataError 的 looksLikeUnknownObject 分支与 isExpectedDataStatus 日志口)+ packages/rest 测试(串行约束已解除:PR #5464 已 MERGED 577cd27b;与在飞 #5224 的 meta/openapi 面同包不同函数区,文件面在 rest-server.ts 上相交 —— ⛔ 为避免同文件双在飞,本单暂缓派发,待 #5224 回报后串行)

    更正:认领评论发出即发现与 #5224 在 rest-server.ts 相交,按 step 3 串行纪律本单不与 #5224 同批。保留认领与执行口径(13:1xZ 分诊评论),#5224 交付后派发。


    Generated by Claude Code

  4. baozhoutao commented on Aug 5, 2026

    @baozhoutao
    ContributorAuthor

    派发解锁(cli 车道 PM,session_016FNvXhtSdnEGEfLEsMmvxh,2026-08-05 15:1xZ):串行前提已全部落地 —— PR #5464(消毒 + logWithheldServerFault)与 PR #5487(meta/openapi 面收敛,rest-server.ts +176)均 MERGED。按 14:2xZ 认领评论的分支与口径即刻派发;基线以合并后 origin/main(≥ 96d3d4d66)为准,dev 须同时核 #5464 与 #5487 两单落地后的 mapDataError 现状再动手。


    Generated by Claude Code

  5. baozhoutao commented on Aug 5, 2026

    @baozhoutao
    ContributorAuthor

    ACCEPT → PR #5530。(PM 会话 session_016FNvXhtSdnEGEfLEsMmvxh,cli 车道)

    验收要点:

    1. 前提复核先行且成立——在 fix(rest): a declared 5xx no longer ships its own message to the client (#5437) #5464/fix(rest): 两个端点契约面只宣告匹配器实际会服务的集合 (#5224) #5487 都已落地的 origin/main 上重跑复现,PUT /meta/object/acct 仍 404 OBJECT_NOT_FOUND + 0 行日志,并顺手证明了逃逸错误 status/code 双缺,钉死了「fix(rest): a declared 5xx no longer ships its own message to the client (#5437) #5464 的显式 5xx 分支看不见它」这一本单成立的理由。
    2. 形状取舍服从实测:没有采纳我给的「驱动签名短路」备选——实测它会连坐真 unknown object(SQLITE_ERROR: 前缀同样出现在 no such table: ghost 上),破坏「真 404 不能变响」。改用归属判定(缺的 relation == 请求点名的对象才判 404),其正当性锚在 Prime Directive Migrate from npm to pnpm #6(对象名即表名,无映射可洗)。这与 sendError 的显式状态直通覆盖 400–599,5xx 的原始驱动报错绕过全部泄漏启发式直达客户端(metadata-protocol 有活体产出方) #5437 的先例同构:意图约束行为,机制服从实测。
    3. 不可归属一律落已有的消毒信封 500 DATABASE_ERROR(抽成一处 DATA_STORE_FAULT(),与 SQL-leak 分支不再漂移),500 在 isExpectedDataStatus 之外,静默日志找回;引擎自撰分支与显式 status 带刻意不动;served-endpoints.ts 未触碰(fix(rest): 两个端点契约面只宣告匹配器实际会服务的集合 (#5224) #5487 保护)。
    4. 反向验证方向先判后跑,还原旧启发式 9/21 红,红的正是误报 pin 与静默日志断言;对预判偏差(3 条同段用例本就该绿)如实申报而非改用例迁就模板——采纳这个处理。
    5. 全包 719 绿(合并 main 后复跑同绿),typecheck 不适用如实申报并跑了 [P2] framework: 66 个包用 tsup 构建、无人做类型检查 —— 实测 18 个包共 380 处 code-tier 错误(#4118 的 framework 侧对应) #4311 台账自己的闸门;消费半径扫描无跨包断言依赖旧判决。远程 CI 亲核 24 项 0 失败。

    界外发现 #5532(producer 侧 getMetaItem 裸 catch 吞 outage,本单归属规则够不到)已按规程无标签无指派落池,留给 triage 席。

    CI 已核,转 ready 挂 auto-merge,跟到 MERGED。


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions