Filed unassigned by the os-dev seat while implementing objectui#8344 (session session_01CZY49skxUBYyJcdnTcYPrE). Grading and domain:* are the triage seat's. ⛔ Not repaired on #8344's PR — that card redirects the node recursion point and its appetite forbids widening.
The defect
AnyComponentSchema is a FLAT z.union over 106 arms. zod builds an invalid_union issue carrying every arm's issue list, and ZodError's constructor eagerly JSON.stringifys the whole tree to fill .message. Once a REFUSED node can appear at a child slot, that nests once per level and the message grows about 25x per level.
Measured on this branch (objectui#8344's redirect applied, zod 4.4.3), with the document { type: 'card', body: [ … ] } nested N deep around one off-spec ui:icon node:
depth 0 safeParse -> refused, message 14,624 chars, 33 ms
depth 1 safeParse -> refused, message 741,330 chars, 33 ms
depth 2 safeParse -> refused, message 18,956,924 chars, 369 ms
depth 3 safeParse -> refused, message 428,269,086 chars, 6,276 ms
depth 4 safeParse -> THROWS RangeError: Invalid string length, after 70,209 ms
⚠️ safeParse THROWS. safeValidateSchema is documented in packages/types/src/zod/index.zod.ts as "Safely validate a schema without throwing errors", and @object-ui/cli's check and validate commands call it on user documents — so a four-deep invalid document crashes the CLI instead of printing a diagnostic.
Controls, so the numbers are readings
Why it is its own card
The fix is a change to how the union reports, not to the recursion point: a z.discriminatedUnion keyed on type (one arm tried per node, no fan-out), or an error map that stops ZodError from materialising every arm's subtree. Both are broad changes to AnyComponentSchema's error shape and would move diagnostics that packages/cli/src/utils/union-arm-diagnostics.ts and several pins read.
⚠️ This is a blocker on objectui#8344, not merely adjacent to it: that card's redirect is what puts a refused node at depth. It is reported in that card's dev report as the reason the PR is parked rather than proposed for merge.
Reproduction
node -e "import('./packages/types/dist/zod/index.zod.js').then(M=>{
const deep=(n)=> n===0 ? {type:'icon',icon:'check',size:'huge'} : {type:'card',body:[deep(n-1)]};
for (const d of [0,1,2,3]) { const r=M.AnyComponentSchema.safeParse(deep(d));
console.log(d, r.success, r.success?0:String(r.error.message).length); }
})"
Generated by Claude Code
Filed unassigned by the
os-devseat while implementing objectui#8344 (sessionsession_01CZY49skxUBYyJcdnTcYPrE). Grading anddomain:*are the triage seat's. ⛔ Not repaired on #8344's PR — that card redirects the node recursion point and its appetite forbids widening.The defect
AnyComponentSchemais a FLATz.unionover 106 arms. zod builds aninvalid_unionissue carrying every arm's issue list, andZodError's constructor eagerlyJSON.stringifys the whole tree to fill.message. Once a REFUSED node can appear at a child slot, that nests once per level and the message grows about 25x per level.Measured on this branch (objectui#8344's redirect applied, zod 4.4.3), with the document
{ type: 'card', body: [ … ] }nested N deep around one off-specui:iconnode:safeParseTHROWS.safeValidateSchemais documented inpackages/types/src/zod/index.zod.tsas "Safely validate a schema without throwing errors", and@object-ui/cli'scheckandvalidatecommands call it on user documents — so a four-deep invalid document crashes the CLI instead of printing a diagnostic.Controls, so the numbers are readings
origin/main(before the redirect) a root-level refusal also produces a 14,624-char message. The flat union's error size is pre-existing AT THE ROOT; what changes it into a cliff is a refused node being reachable at depth.Why it is its own card
The fix is a change to how the union reports, not to the recursion point: a
z.discriminatedUnionkeyed ontype(one arm tried per node, no fan-out), or an error map that stopsZodErrorfrom materialising every arm's subtree. Both are broad changes toAnyComponentSchema's error shape and would move diagnostics thatpackages/cli/src/utils/union-arm-diagnostics.tsand several pins read.Reproduction
Generated by Claude Code