Skip to content

Conversation

@sentik
Copy link
Contributor

@sentik sentik commented Nov 3, 2025

Fix: Correct handling of allOf with strict mode in Zod schemas

Summary

This MR fixes an issue where Zod schema generation from OpenAPI specifications with allOf containing multiple object schemas produced incorrect validation code when strict mode was enabled. Previously, .strict() was applied to each object separately and then combined using .and(), which caused validation issues. Now, all object properties are merged into a single object, and strict validation is applied only once to the merged schema.

Fixes #2520

Problem

When generating Zod schemas from OpenAPI specifications with allOf containing multiple object schemas and strict mode enabled, the generator produced invalid Zod code:

// ❌ Before (incorrect):
zod.object({ "name": zod.string() }).strict().and(zod.object({ "swimming": zod.boolean() }).strict())

This caused validation issues because:

  • Strict mode was applied to intermediate schemas instead of the final merged schema
  • The .and() method creates an intersection that doesn't properly validate unknown keys in strict mode
  • Each object schema was validated separately, allowing unknown keys that should be rejected

Example OpenAPI Schema

schema:
  allOf:
    - type: object
      required: ["name"]
      properties:
        name:
          type: string
    - type: object
      required: ["swimming"]
      properties:
        swimming:
          type: boolean

Solution

The fix detects when all parts of an allOf schema are objects and strict mode is enabled. In such cases, it merges all object properties into a single object and applies .strict() (or strictObject for Zod v4) only once to the final merged schema.

// ✅ After (correct):
zod.object({
  "name": zod.string(),
  "swimming": zod.boolean()
}).strict()

Implementation Details

Core Logic Changes

The fix modifies the parseZodValidationSchemaDefinition function in packages/zod/src/index.ts:

  1. Detection: Checks if all parts of an allOf schema are objects when strict mode is enabled
  2. Property Merging: Combines properties from all constituent objects into a single mergedProperties object
  3. Single Strict Application: Applies .strict() once to the merged object (or uses strictObject for Zod v4)

Design Decisions

  1. Why merge instead of keeping .and()?

    • Strict mode must be applied to the final merged schema, not intermediate ones
    • Merging produces a single object schema with all properties, which strict mode can validate correctly
    • The .and() method creates intersections that don't properly handle unknown keys in strict mode
  2. Why conditional merging?

    • Only merges when all parts are objects AND strict mode is enabled
    • Falls back to .and() for non-object schemas or when strict mode is disabled
    • Maintains backward compatibility with existing behavior
  3. Why support both Zod v3 and v4?

    • Zod v3 uses zod.object().strict() pattern
    • Zod v4 introduces zod.strictObject() as a single method
    • Supporting both ensures compatibility with existing projects and future versions

Changes Made

1. Core Implementation (packages/zod/src/index.ts)

  • Modified parseZodValidationSchemaDefinition to detect and handle object-only allOf schemas
  • Added property merging logic for strict mode scenarios
  • Added support for both Zod v3 and v4 object creation methods

2. Unit Tests (packages/zod/src/zod.test.ts)

Added comprehensive test cases:

  • handles allOf with strict mode for objects (issue #2520) - tests Zod v3 behavior
  • handles allOf with strict mode for objects in Zod v4 - tests Zod v4 behavior

Both tests verify:

  • Properties are merged correctly
  • Strict is applied only once
  • No .and() is used in the output

3. Integration Test

  • Created OpenAPI specification: tests/specifications/all-of-strict.yaml
  • Added configuration all-of-strict in tests/configs/default.config.ts:

Related Issues

Fixes #2520

… strict mode is enabled

Fix incorrect strict mode handling for allOf schemas by merging object properties into a single object instead of applying strict() to each part separately.
This ensures proper validation behavior.
@sentik
Copy link
Contributor Author

sentik commented Nov 3, 2025

endpoints.ts for all-of-strict.yaml (example from #2520)

endpoints.txt

@melloware melloware merged commit cdcdfe1 into orval-labs:master Nov 3, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zod: Strict with allOf OpenAPI is not producing usable zod schemas

2 participants