Skip to content

fix(zod): top-level null mismatch in makeJsonSchema() for required Json fields #2647

@coderabbitai

Description

@coderabbitai

Summary

makeJsonSchema() in packages/zod/src/factory.ts currently includes z.null() in its top-level union, meaning it accepts null at runtime for any Json field — including required ones. However, the JsonValue type introduced in #2641 excludes null at the top level, so z.infer<> reports JsonValue while null still parses successfully, silently violating the TypeScript type.

Impact

  • Required Json fields: type says JsonValue (non-null), but runtime accepts null → silent type/runtime mismatch.
  • Optional Json? fields: benign — the ZodNullable wrapper already covers top-level null.

Proposed Fix

Split makeJsonSchema() into a base variant (no top-level z.null()) and a recursive item variant (JsonValue | null), used inside z.array(...) and z.object({}).catchall(...):

// factory.ts
private makeJsonItemSchema(): z.ZodType {
    return z.union([this.makeJsonSchema(), z.null()]);
}

private makeJsonSchema(): z.ZodType<JsonValue> {
    return z.union([
        z.string(),
        z.number(),
        z.boolean(),
        // z.null() removed — null at root is handled by nullable wrapper for Json? fields
        z.array(z.lazy(() => this.makeJsonItemSchema())),
        z.object({}).catchall(z.lazy(() => this.makeJsonItemSchema())),
    ]);
}

References

Requested by @ymc9.

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