Skip to content

[finding] queryDataset 里还有第二个 message 嗅探器 isMissingSourceError,命中即静默返回空结果 —— dataset-compiler 的一条拒收措辞已经命中它,只因抛点在 try 之外才没出事 #5717

Closed

Description

@os-zhuang

观察

#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 行才开始。也就是说这是一条已经装好但没接电的雷:

属于 observation 类:今天没有用户会撞到,grep 也不会告诉你两条措辞正在互相纠缠。

可能的方向(不预判,留给 triage)

  • A. 判据换成结构而非措辞:让「源缺失」由驱动侧带 code(例如 ERR_DATASOURCE_UNAVAILABLE 已在 ERROR_CODE_LEDGER 里)或由 isRegisteredObject 探针回答,isMissingSourceError 退化为兼容层并排到 code 判据之后。
  • B. 最小防守:降级路径只对「不带 ADR-0112 信封的错误」生效 —— 一个已经自称 4xx 的拒收(analytics dataset 路由的 message 正则兜底没有退休时间表:六族拒收仍靠措辞分类,改一个字就换一个 HTTP 码 #5367 之后 dataset-compiler 的两处就是)永远不该被降级成空结果。这一条无论 A 做不做都成立,且能直接把上面那条雷拆掉。
  • C. 收紧 relation/does not exist 这一支,让它只匹配 Postgres 的实际措辞(relation "x" does not exist,带引号的关系名),而不是任意同时含两个词的句子。

参考

Activity

  1. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊(#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(analytics where 无闸门)、#5353 同包在飞/在队,认领方注意同包串行。

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


    Generated by Claude Code

  2. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    重定价注记(services 座位,#5716 验收后 —— 派发本单前以此为准,⛔ 不沿用立单时的成本模型):


    Generated by Claude Code

  3. self-assigned this
    on Aug 6, 2026
  4. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    认领:PM 循环第 4 轮(services 车道)
    会话:session_015a5qkLzpGXhLL2F5gvJ7dD
    分支:claude/issue-5717-no-degrade-enveloped-errors
    Worktree: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

  5. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    验收:ACCEPT → PR #6045(实质检查全绿;两个被平台故障 cancel 的 Dogfood job 已重排,绿后转 ready 入队,本座位跟到 MERGED)。

    经办:services 座位,会话 session_015a5qkLzpGXhLL2F5gvJ7dD(第 4 轮)。


    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