Repository navigation
analytics dataset 路由:另有九处「作者/调用方形状」的 dataset 拒收仍答 500 —— 它们从来没进过 #5352 的正则名单,所以 #5367 的信封化也没覆盖到 #5716
Description
Activity
存量裁决轮(维护者 2026-08-06 委托,session_01LeEfA7CFwbJb7JJmXm2KM3):裁定入队。前提经 origin/main 核实仍成立:清单九处作者/调用方形状拒收仍是裸
throw new Error(走 500)——dataset-compiler.ts 三处、dataset-executor.ts 四处等;#5367 建的datasetInvalidError(DATASET_INVALID/400)落点已在,逐处判 code + 端到端断言 + changeset 即可收口。维护者可否决。
Generated by Claude Code
实现 #5739(即席推断路径的关系穿越)时路过,带来一处清单外的同族站点,记录在此而不另立单——本单就是这个族的归属,补进清单比开孪生单干净。
站点
packages/services/service-analytics/src/strategies/objectql-strategy.ts,planCrossObject的三处拒收(跨对象 filter / 跨对象 measure / 跨对象 time dimension bucket),以及同函数的多跳与不可重组 measure 两处。全部是裸throw new Error(...),不带 code/status。触发者是调用方形状:query 里写了一个关系穿越成员而驱动是 ObjectQL(engine.aggregate 不能 join)。与清单里
native-sql-strategy.ts:468(cube 上没有这个 measure)同层同性质。实测(含 #5739 改动的分支)
cube: crm_account(即席推断),ObjectQL 策略 where: {'owner.region': 'NA'} → Error: [Analytics] ObjectQLStrategy cannot evaluate a cross-object filter ("owner.region") — the engine cannot join in an aggregate. … code: undefined status: undefined与 #5739 的关系(可见性上升,不是新缺陷)
这条 throw 在
origin/main上就已经可达:已注册 cube 上对同一个成员就是它,即席路径上数组 where 写法也是它。#5739 裁 B 落地后,即席路径的对象写法也收敛到同一条 throw(此前它要么静默筛基表同名列、要么答400 INVALID_FIELD点名剥出来的尾段)。也就是说 #5739 让这条无信封的拒收在一条常见写法上变得更容易撞到,但它不是 #5739 引入的,也不该由 #5739 的 PR 顺手改(那会把信封变更混进一个 JOIN 语义的 PR)。
建议
按本单既有判据处理即可:调用方形状 ⇒ 4xx。code 上,跨对象拒收更像
DATASET_INVALID的同形兄弟还是INVALID_FIELD(它确实点名了一个成员),留给本单实施者一并判。若维护者认为超出本单圈定的「九处」,我可以另立单——只是那样会把同一族拆成两单。
Generated by Claude Code
认领:PM 循环第 2 轮(services 车道)
会话:session_015a5qkLzpGXhLL2F5gvJ7dD
分支:claude/issue-5716-dataset-refusals-4xx
Worktree:objectstack-issue-5716
域:domain:services
文件面:packages/services/service-analytics/src/dataset-compiler.ts/dataset-executor.ts/strategies/native-sql-strategy.ts/strategies/objectql-strategy.ts(planCrossObject 族,按 11:40Z 留痕并入,PM 批准)/dataset-refusal.ts+ 同包测试 +packages/rest信封端到端断言;⛔ 不触analytics-service.ts(#5923 刚合面)、不触默认粒度逻辑(#5688 靶面)、不触 read-scope-sql(#5808 已另判 500)
串行约束已清:#5808 今日已合(read-scope 开放问题已闭);#5923(#5739)在合并队列 —— 开工前先确认其落地、基于合并后 origin/main;⚠️ #5688 与本单同文件(dataset-executor.ts),严格后轮串行范围注记:planCrossObject 族(3+2 处)按本单自身判据「调用方形状 ⇒ 4xx」并入清单 —— 同族一单收口,不开孪生单。
Generated by Claude Code
验收:ACCEPT → PR #5963(CI 24/24 绿,随后转 ready 入队,本座位跟到 MERGED)。
- 12 抛点 / 13 拒收条件信封化,含 PM 批准并入的 planCrossObject 族;逐处判码带理由(DATASET_INVALID vs INVALID_FIELD 的分界写进 dataset-refusal.ts 头注释:判决对象是 dataset/整个 selection 还是请求点名的成员;
member取请求拼写),invalidMemberError未新增 error code。消息一字未动(fix(service-analytics): 即席推断的 Cube 把owner.region当成关系穿越,不再铸成基表列region(#5739) #5923 断言与 [finding]queryDataset里还有第二个 message 嗅探器isMissingSourceError,命中即静默返回空结果 ——dataset-compiler的一条拒收措辞已经命中它,只因抛点在 try 之外才没出事 #5717 嗅探雷都未被扰动)。 - 两处清单偏差均为实测支撑的修正,接受:①「measure type 不认识」被证明是内部不变量(Metric.type 闭合枚举 +
metric-type-coverage恰好划分 pin)—— 按 issue 自己的判据留 500 加 pin,九处改八处是对的输出;② MAX_JOIN_HOPS 双路径一致化 + 端到端据实断言 schema 的 400,不造不存在的路径。 - 机制假设 2 证伪到好的方向,公开记账:/analytics/query 的
errorResponseBase自 analytics /query 未做 cube 存在性校验,未注册名直达驱动当表名;且错误路径原样回显驱动 SQL(#3770 同类,另一子系统) #3867/The dispatcher puts the HTTP status inerror.codeand parks the real code indetails— pinned in #3687, still unfixed #3842 起就采纳 err.status/code —— BEFORE 500/INTERNAL_ERROR → AFTER 400/INVALID_FIELD 立即收敛,无中间态;派发令预设的「/analytics/query仍把 RLS 策略字段名回显给调用方 —— read-scope 拒收的泄漏在姐妹面上没堵,#5367 只堵了 dataset 路由 #5811 落地才收敛」不成立,该假设错在 PM。路由照令一行未动。 - 三条旧代理 pin(
not.toBe('INVALID_FIELD'))换成更强的承重断言(消息归属 +err.field缺席判据 + member/param 正向)—— fixture 三分法的正确用法,反向验证里它们随 producer revert 一起变红。 - 收益穿边界已双路由实测(真实 handler 驱动 BEFORE/AFTER)—— rest-server 的 4xx 直通把 ≥500 字符的 message 整条换成 "Request failed" —— #5368 刚写好的过滤器拒收措辞,客户端一个字也收不到(实测) #5423 教训的主动应用,好。
- changeset 按 analytics 的 filter 拒收到不了调用方:service 侧多数拒收没有 ADR-0112 信封,REST 面又用 message 正则嗅探,一律答 500 #5352 范式逐行列 13 种拒收的新码,合格。
必答项三条已核;#5717 的重定价已单独记到该单。
经办:services 座位,会话
session_015a5qkLzpGXhLL2F5gvJ7dD(第 2 轮)。
Generated by Claude Code
- 12 抛点 / 13 拒收条件信封化,含 PM 批准并入的 planCrossObject 族;逐处判码带理由(DATASET_INVALID vs INVALID_FIELD 的分界写进 dataset-refusal.ts 头注释:判决对象是 dataset/整个 selection 还是请求点名的成员;
- added a commit that references this issue
on Aug 7, 2026 - added a commit that references this issue
on Oct 7, 2026
背景
#5352 / PR #5366 让
POST /analytics/dataset/query的 catch 先读 ADR-0112 信封,并留下一串写死的 message 正则兜底(六族)。#5367 / PR(本单的来源)把其中五族信封化成DATASET_INVALID/ 400 并删掉对应正则条目,只留read-scope-sql一条。实现 #5367 时逐条读了
service-analytics的全部throw new Error(...),发现一个 #5352 和 #5367 都没覆盖的集合:另有九处同样是调用方 / dataset 作者形状的拒收,它们既没有信封、也从来没进过那串正则,所以今天答的是500 ANALYTICS_QUERY_FAILED—— 与已修好的五族是同一个缺陷,只是当初没被点名,因此也没有被任何退休时间表接管。清单(行号基于 origin/main + #5367 的提交)
packages/services/service-analytics/src/dataset-compiler.tsinclude里的 relationship 在对象上不存在include路径超过MAX_JOIN_HOPS跳数上限packages/services/service-analytics/src/dataset-executor.tsdateRange里的日期解析不出来selection.timeDimensions[].dateRange)compareTo.dimension点名的 timeDimension 没有dateRangeselection.compareTo)compareTo但整个 selection 没有任何带dateRange的 timeDimensioncompareTo.dimension有歧义(多个 timeDimension 带dateRange)packages/services/service-analytics/src/strategies/native-sql-strategy.tsCOUNT(*)静默兜底改成拒收的那一处)selection.measures)type不认识七处的输入直接来自 HTTP 请求体的
selection,两处来自调用方内联 POST 的 dataset(Studio 预览)或作者保存的 dataset。全部符合 4xx。为什么值得单独立一条
compareTo没给日期窗、include写了个不存在的 relationship —— 作者读到的是500 ANALYTICS_QUERY_FAILED(「平台坏了」),ops 告警按 5xx 计数。这正是 analytics 的 filter 拒收到不了调用方:service 侧多数拒收没有 ADR-0112 信封,REST 面又用 message 正则嗅探,一律答 500 #5352 的立单理由,只是换了一批错误。packages/services/service-analytics/src/dataset-refusal.ts的datasetInvalidError(DATASET_INVALID/ 400),analytics dataset 路由的 message 正则兜底没有退休时间表:六族拒收仍靠措辞分类,改一个字就换一个 HTTP 码 #5367 建的。逐处判一次 code(多数是DATASET_INVALID;468 那处也许更贴INVALID_FIELD,与assertMeasureFields/assertDimensionFields的 analytics: a measure naming a missing field 500s with SQLITE_ERROR instead of a 400 naming the field #4437 / [17.0-rc2验收] analytics: 不存在的 dimension 500(泄漏 SQL / SQLITE_ERROR)而不是 400 指名字段 —— #4437 只给 measure 加了闸门,dimension 侧对称缺口仍在 #5520 同形),然后加端到端断言即可。明确不在此列
dataset-compiler.ts:135(non-derived measure 没有 aggregate)与native-sql-strategy.ts:139(Cube not found)是内部不变量,spec refinement / 上游注册已经保证;到达即我们的 bug,500 是诚实答案。analytics dataset 路由的 message 正则兜底没有退休时间表:六族拒收仍靠措辞分类,改一个字就换一个 HTTP 码 #5367 的dataset-refusal.ts头注释已把这条界线写下,并有一条 pin 测试。read-scope-sql.ts十处 fail-closed:输入是管理员写的 RLS 策略,不是调用方输入,正确的 code/status 是 analytics dataset 路由的 message 正则兜底没有退休时间表:六族拒收仍靠措辞分类,改一个字就换一个 HTTP 码 #5367 单独留的开放问题,不要一起做。验收建议
datasetInvalidError或按需新增同形构造器抛出。packages/rest/src/analytics-dataset-refusal-envelope.test.ts。