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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions