Skip to content

JSON Schema generator emits invalid schemas: spurious {} oneOf branch for union aliases + dangling $refs in notifications.schema.json #302

Description

@colbylwilliams

Two related, pre-existing correctness problems in the JSON Schema generator (scripts/generate-json-schema.ts). Both are repo-wide (not specific to any one type) and were surfaced by review on #298. The generated schemas are reference/documentation artifacts today — there is no CI gate that resolves $refs or runs strict oneOf validation, which is why these have gone unnoticed — but any consumer that validates payloads against them will hit both.

1. Spurious empty {} branch in oneOf for union type aliases

Every union type alias is emitted with a leading empty-schema branch, e.g.:

"CanvasProviderSource": {
  "oneOf": [
    {},
    { "$ref": "#/$defs/CanvasServerProviderSource" },
    { "$ref": "#/$defs/CanvasClientProviderSource" }
  ]
}

Because {} matches any value, every instance matches ≥2 oneOf branches, so strict oneOf validation always fails.

Root cause: a union type alias's getTypeNode().getText() includes the leading | (i.e. | A\n | B). splitUnionType then produces an empty-string first element, and typeTextToSchema('') falls through to return {}. The union handler filters out 'undefined' parts but not empty-string parts.

Scope: state.schema.json (15 $defs — 14 on main before the canvas addition) and actions.schema.json (17 $defs). Affected unions include ToolCallState, ToolCallConfirmationState, SessionInputRequest, JsonPrimitive, ChatOrigin, MessageAttachment, ResponsePart, ToolResultContent, Customization, McpServerState, TerminalContentPart, CanvasProviderSource, …

Likely fix: drop empty parts in the union branch, e.g. parts.filter(p => p !== 'undefined' && p !== '') (or strip a leading | before splitting).

2. Dangling $refs in notifications.schema.json

notifications.schema.json references ~95 $defs it never defines (e.g. ToolCallState, SessionInputRequest, MessageAttachment, URI, JsonPrimitive, ChatOrigin, every *Kind/status enum member, and now CanvasProviderSource), leaving unresolved $refs and making the file non-self-contained / invalid for standalone validation.

Root cause: the notifications schema generator only inlines a subset of $defs (the interfaces it directly walks) and does not transitively pull in referenced type aliases/enums the way the state/actions generators do.

Likely fix: transitively collect and inline every referenced $def, or $ref them into a shared/base schema document.

Note

Discovered while reviewing #298 (Canvas channel). The canvas types (CanvasProviderSource, CanvasState) reproduce the identical pre-existing pattern, so they are intentionally left following the established convention in that PR and tracked here for a focused, generator-wide fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssue identified by VS Code Team member as probable bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions