Skip to content

休眠:invalid_key / invalid_element 把真实 issue 挂在 issue.issues 上,union 家族的三个消费者一个都不下降 #5389

Description

@baozhoutao

观察类记录(今天点不着,无用户可见影响)。从 #5341 的正文里抽出来单独存档 —— 那单被 PR 关掉之后,这条注记就埋在一个 closed issue 里了。

事实

invalid_union 把分支的真实 issue 挂在 issue.errors[] 上,这个家族已经修了三次:

消费者 文件 状态
formatZodError(spec,defineStack 抛错走它) packages/spec/src/shared/error-map.zod.ts #4971 / PR #5342
zodIssuesToFields(REST wire) packages/rest/src/rest-server.ts #5014 / PR #5362
formatZodErrors(CLI 终端) packages/cli/src/utils/format.ts #5341

同样的形状还有另外两个 issue code,三个消费者一个都没处理:

  • invalid_key —— z.record(K, V) 的键 schema 失败,真实 issue 在 issue.issues 上(注意是 issues,不是 union 的 errors);
  • invalid_element —— map / set 的元素 schema 失败,同上。

三处消费者读的都只有顶层 issue 与(#5341 之后)issue.errors,没有任何一处读 issue.issues。所以这两种 code 一旦出现,作者/调用方拿到的就是裸的一行,散文全部丢在 payload 里 —— 与 #4971 / #5014 / #5341 是同一个缺陷,只是入口不同。

为什么今天点不着

packages/spec 里 z.record(...) 的键 schema 目前全是 z.string() 或 enum:

  • z.string() 键永远不失败;
  • enum 键的坏键走的是顶层 unrecognized_keys(实测过),不产生 invalid_key。

map/set 在 authoring 面同样没有。所以现在没有任何 authoring 面能产出这两种 code,用户今天遇不到,不构成缺陷。

什么时候会点着

任何人写下第一个带约束的记录键,例如 z.record(SnakeCaseIdentifierSchema, X) —— 从那一刻起,该 slot 上的坏键在终端、wire、defineStack 三条路上都会退化成一行没有信息的判决,而三份代码不会有任何一处报警。

建议(不急)

两条路,选一条:

  1. 在三个消费者里把 issue.issues 与 issue.errors 一并下降(CLI 侧现在是 import spec 的 formatZodIssue,所以真正要改的只有 spec 一处 + REST 一处);
  2. 或者加一条 gate:packages/spec 内 z.record(...) 的键 schema 必须是 z.string() 或 enum,想放宽时先把 (1) 做掉。

(2) 更符合「先把不会写错做成结构性的」这条口径,但 (1) 是真正把家族补完。谁先动谁定。

记录人:#5341 的实现(domain:cli)。未认领,无 pm:queue,交 PM 定级。

Activity

  1. baozhoutao commented on Aug 5, 2026

    @baozhoutao
    ContributorAuthor

    分诊(cli 车道 PM,session_016FNvXhtSdnEGEfLEsMmvxh,2026-08-05):持有(留 finding)。为什么还留着:休眠形状,正文自证今日不可达(spec 内 z.record 键 schema 全为 z.string()/enum,enum 坏键走 unrecognized_keys 不产 invalid_key)。晋级路径倾向正文方案 (2)——先加「record 键 schema 限 string/enum,放宽前须先做 (1)」的 gate,把不可达变成结构性保证;该 gate 落 packages/lint/spec scripts,属 spec-tooling/devx 车道,非本车道。下轮分诊轮复核。


    Generated by Claude Code

  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    发现分诊轮:持有。休眠确认成立(spec 内 record 键 schema 全为 string/enum,invalid_key/invalid_element 今天不可产出),重启条件:第一个带约束的 record 键落仓,或有人做 (1) 家族补完 / (2) 键 schema gate —— 届时晋级入队。本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. claude commented on Aug 6, 2026

    @claude
    Contributor

    发现分诊轮:持有(维持 08-05 判级)+ 补域 domain:spec(前两轮均未打域标签,本轮补齐)。

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


    Generated by Claude Code

  4. claude commented on Aug 7, 2026

    @claude
    Contributor

    Findings triage — verdict: HOLD stands (keeps finding, domain:spec). This round re-measured the dormancy premise rather than restating it, because that premise is the entire basis of the hold and it decays with every spec change.

    Dormancy re-measured on origin/main @ 1363084 — still holds

    The claim under test: every z.record(...) key schema inside packages/spec is z.string() or an enum, so neither invalid_key nor invalid_element can be produced today. Enumerated every non-z.record(z.string() occurrence and resolved each key schema to its definition:

    Site Key schema Resolves to Can emit invalid_key?
    api/rest-server.zod.ts:241, :397 CrudOperation z.enum([...]) (:175) No — bad key ⇒ top-level unrecognized_keys
    data/driver.zod.ts:181 DateGranularity z.enum(['day',…,'year']) (query.zod.ts:122) No — same
    system/translation.zod.ts:626 LocaleSchema z.string().describe('BCP-47 …') (:12) No — z.string() keys never fail
    system/core-services.zod.ts:241 CoreServiceName enum No
    system/auth-config.zod.ts:189 z.string() — No
    data/object.zod.ts:682 (retention.onlyWhen) z.string() — No
    data/driver/memory.zod.ts:231 (initialData, incl. nested) z.string() — No

    No map/set on the authoring surface either. invalid_key / invalid_element remain unproducible ⇒ dormant ⇒ held. The remaining z.record( hits in the grep are prose in docblocks and tests about FlowNodeSchema.config (z.record(z.unknown()), deliberately open), not new key schemas.

    LocaleSchema deserves the callout because it is the one that looks like it should be constrained — a BCP-47 tag keying TranslationBundleSchema. It is a bare z.string() carrying its constraint only in .describe(). That is exactly the shape that would arm this finding the day someone tightens it, which makes it the most likely trigger of restart condition ① and worth naming here so the next reader does not re-derive the table.

    Restart conditions (unchanged in substance):

    1. the first constrained record key lands in spec (z.record(SnakeCaseIdentifierSchema, X), a tightened LocaleSchema, or similar) ⇒ the shape is armed on that slot in all three consumers at once, silently ⇒ promote immediately;
    2. someone completes route (1) — descend issue.issues alongside issue.errors in packages/spec/src/shared/error-map.zod.ts and packages/rest/src/rest-server.ts — or adds route (2)'s key-schema gate.

    Dedup: the three same-family issues #4971 / #5014 / #5341 are all closed and fixed invalid_union's issue.errors; this is the other two codes on the same shape, a different entry, not a duplicate.

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


    Generated by Claude Code

  5. os-project-manager commented on Aug 8, 2026

    @os-project-manager
    Collaborator

    Findings sweep (maintainer-authorized one-off, 2026-08-07 — registered on #6015): promoted to the queue. invalid_key / invalid_element are the 4th/5th members of a family already fixed three times (#4971, #5014, #5341) in exactly the three consumers this card names — scoped, precedented, and the DX failure mode (top-level issue with the real error buried) is identical. finding → pm:queue. Triage may split per consumer if preferred.


    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

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions