## 观察 #5352 / #5367 处理的是 `rest-server.ts` 里那串「按 message 措辞决定 HTTP 码」的正则。`packages/services/service-analytics/src/analytics-service.ts` 里还有**第二个同类嗅探器**,而且它的后果比选错状态码更重:命中即把错误换成一个空结果。 ``` // analytics-service.ts:83 function isMissingSourceError(err: unknown): boolean { const msg = String((err as { message?: unknown })?.message ?? err ?? '').toLowerCase(); return ( msg.includes('no such table') || (msg.includes('relation') && msg.includes('does not exist')) || msg.includes("doesn't exist") || msg.includes('not registered') || msg.includes('unknown object') || msg.includes('is not a registered object') ); } ``` 命中后 `queryDataset` 的 catch(`analytics-service.ts:700`)会 `logger.warn` 一句然后 `return { rows: [], fields: [], totals: [] }` —— 也就是 widget 画一个自信的空图,而不是报错。 这个降级本身是刻意的(对象没在本 kernel 挂载时 widget 应当显示「无数据」而非 500),#5033 已经把跨库那一支拆出来单独响亮报错。问题在于**判据是措辞**。 ## 已经存在的一次命中(今天无害) `dataset-compiler.ts:260` 的拒收原文: > `[dataset-compiler] dataset "X" includes relationship "R" which does not exist on object "O".` 它同时含 `relation`(在 `relationship` 里)和 `does not exist` → `isMissingSourceError` 返回 **true**。 今天不出事的唯一原因是**抛点在 try 之外**:`queryDataset` 先 `const compiled = this.registerDataset(dataset)`(第 626 行,`compileDataset` 在这里抛),而那个 try/catch 在第 697 行才开始。也就是说这是一条已经装好但没接电的雷: - 把编译移进 try(或未来任何一处 executor / strategy 的拒收措辞里出现「relation…does not exist」),该拒收就会被吞成空结果 —— 无异常、无 4xx、无 5xx,只有一条 warn 日志和一张空图。这正是 #5033 立单时描述的「populated table 上 Total Spend: 0」那一类症状。 - 反方向也成立:驱动侧一条真正的「表不存在」如果换了措辞(不同驱动/不同版本),降级失效,widget 变 500。 属于 observation 类:今天没有用户会撞到,`grep` 也不会告诉你两条措辞正在互相纠缠。 ## 可能的方向(不预判,留给 triage) - **A. 判据换成结构而非措辞**:让「源缺失」由驱动侧带 code(例如 `ERR_DATASOURCE_UNAVAILABLE` 已在 `ERROR_CODE_LEDGER` 里)或由 `isRegisteredObject` 探针回答,`isMissingSourceError` 退化为兼容层并排到 code 判据之后。 - **B. 最小防守**:降级路径只对「不带 ADR-0112 信封的错误」生效 —— 一个已经自称 4xx 的拒收(#5367 之后 `dataset-compiler` 的两处就是)永远不该被降级成空结果。这一条无论 A 做不做都成立,且能直接把上面那条雷拆掉。 - **C. 收紧 `relation`/`does not exist` 这一支**,让它只匹配 Postgres 的实际措辞(`relation "x" does not exist`,带引号的关系名),而不是任意同时含两个词的句子。 ## 参考 - `packages/services/service-analytics/src/analytics-service.ts:83`(`isMissingSourceError`)、`:112`(`missingSourceRelation`)、`:626`(编译点)、`:697`-`:730`(降级 catch) - `packages/services/service-analytics/src/dataset-compiler.ts:260`(命中的那条措辞) - #5033(降级过宽导致 cross-datasource 被藏起来,已收窄)、#5352 / #5367(同一个「按措辞分类」反模式在 REST 面的两轮清理)
Activity
os-zhuang commentedon Aug 6, 2026
发现分诊(#4949 纪律):晋级,摘
finding换pm:queue,domain:services已在(落点packages/services/service-analytics/src/analytics-service.ts)。一行理由:已核 origin/main —— 嗅探器(:83)与命中措辞(
dataset-compiler.ts:262)均仍在,「装好没接电的雷」成立;方向 B(带 ADR-0112 信封自称 4xx 的拒收永不降级成空结果)是自包含、无论 A 做不做都成立的最小拆雷,#5352/#5367 同反模式清理先例在前 —— 值得现在拆,不值得等踩响。范围限 B(+C 可选收紧措辞支);方向 A(驱动侧结构化 code)涉 drivers 面,需要时独立立单。与 #5669(analyticswhere无闸门)、#5353 同包在飞/在队,认领方注意同包串行。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
hotlong commentedon Aug 6, 2026
重定价注记(services 座位,#5716 验收后 —— 派发本单前以此为准,⛔ 不沿用立单时的成本模型):
isMissingSourceError嗅探器仍在(analytics-service.ts 的 queryDataset catch);dataset-compiler 的 "relationship … does not exist" 措辞未改,仍命中嗅探器;抛点仍在 try 之外 —— 那颗「装好没接电的雷」状态不变。Generated by Claude Code
hotlong commentedon Aug 6, 2026
认领:PM 循环第 4 轮(services 车道)
会话:
session_015a5qkLzpGXhLL2F5gvJ7dD分支:
claude/issue-5717-no-degrade-enveloped-errorsWorktree:
objectstack-issue-5717域:
domain:services文件面:
packages/services/service-analytics/src/analytics-service.ts(isMissingSourceError降级路径,现 :92/:796)+ 同包测试;⛔ 不触buildQuery/粒度判据(#6003 刚合面)、不触各拒收 throw 本体(#5963 面)、不触 drivers(方向 A 出界,分诊已限定)串行约束已清:#6003 今日刚合(同文件,行号已移),基于合并后 origin/main;同包无其它在飞(#6007 待分诊,不同函数)
执行口径:分诊晋级评论限定范围 B(+C 可选);重定价注记(13:19Z)在案 —— #5963 之后 compiler 站点已带信封,B 从今天起覆盖它。派发令随后。
Generated by Claude Code
hotlong commentedon Aug 6, 2026
验收:ACCEPT → PR #6045(实质检查全绿;两个被平台故障 cancel 的 Dogfood job 已重排,绿后转 ready 入队,本座位跟到 MERGED)。
status<500的偏离 —— 理由实测充分且与 issue 原文一致:已声明的 5xx(fail-closed 的READ_SCope_COMPILE_FAILED)被吞成空图比 400 更糟,「生产者已回答分类问题」才是 B 的本义;论证写进代码注释与 changeset,合格。missingSourceRelation同模式,「是否缺」与「缺什么」不再可能分歧。??别名读法 (#5017) #5046 教训)。isMissingSourceError仍把 Postgres 的「缺列」措辞column "c" of relation "t" does not exist判为「缺源」——文档明说不该,今天靠读路径不产生该措辞而无害 #6035(缺列措辞内含缺表短语的休眠残留)按纪律立案并在源码注释指名。经办:services 座位,会话
session_015a5qkLzpGXhLL2F5gvJ7dD(第 4 轮)。Generated by Claude Code