Profiles·Public

zod

semver>=3.0.0postconditions15functions8last verified2026-06-24coverage score100%

Postconditions: what we check

  • parse · parse-validation-error
    error
    WhenWhen input data does not match the schema definition
    ThrowsZodError
    Required handlingCaller MUST wrap parse() in try-catch block or use safeParse() instead. ZodError contains detailed validation failure information in the 'issues' array.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • parse · parse-type-coercion-error
    error
    WhenWhen type coercion fails (e.g., z.coerce.date() receives invalid date string)
    ThrowsZodError
    Required handlingCaller MUST handle coercion failures. Use safeParse() or try-catch.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • parse · parse-async-schema-error
    error
    Whenschema.parse() is called on a schema that contains async refinements (.refine(async fn)) or async transforms (.transform(async fn)). The synchronous parse() path detects a pending Promise and throws immediately instead of returning the result.
    Throws$ZodAsyncError: Encountered Promise during synchronous parse. Use .parseAsync() instead.
    Required handlingSchemas with async refinements/transforms MUST use parseAsync() or safeParseAsync(). If you have a schema that sometimes has async validators, always use the async variant: // ❌ WRONG const result = schema.parse(data); // ✅ CORRECT const result = await schema.parseAsync(data); Catch blocks checking `instanceof z.ZodError` will NOT catch this error — it is a plain Error, not a ZodError. Add a separate check or catch all.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][4]
  • parseAsync · parse-async-validation-error
    error
    WhenWhen input data does not match the schema definition or async refinements fail
    ThrowsZodError
    Required handlingCaller MUST wrap parseAsync() in try-catch block or use safeParseAsync() instead. Handle rejected promises appropriately.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • parseAsync · parse-async-refinement-error
    error
    WhenWhen custom async refinement validation fails
    ThrowsZodError
    Required handlingCaller MUST handle async refinement failures. The error.issues array will contain details about which refinements failed.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • safeParse · safe-parse-success-check
    warning
    WhenWhen validation completes (success or failure)
    Throwsnever
    Required handlingCaller MUST check result.success before accessing result.data or result.error. TypeScript discriminated unions enforce this at compile time.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6]
  • safeParseAsync · safe-parse-async-success-check
    warning
    WhenWhen async validation completes (success or failure)
    Throwsnever
    Required handlingCaller MUST check result.success before accessing result.data or result.error. Handle the promise appropriately.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6]
  • safeParseAsync · safe-parse-async-refinement-throw
    warning
    Whenschema.safeParseAsync() is called on a schema whose async refinement (.refine(async fn)) THROWS an error (rather than returning false). The throw is NOT caught by the "safe" wrapper — it propagates as a promise rejection with the original error type (not a ZodError, not a {success:false} result).
    ThrowsOriginal Error from the throwing async refinement body (not wrapped in ZodError)
    Required handlingEven when using safeParseAsync(), still wrap in try-catch when the schema uses async refinements that can throw: // wrong (assumes safeParseAsync never rejects) const result = await schema.safeParseAsync(data); if (!result.success) { /* handle ZodError */ } // correct try { const result = await schema.safeParseAsync(data); if (!result.success) { /* handle ZodError */ } } catch (err) { // async refinement threw (e.g., DB error) }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • encodeAsync · encode-async-unidirectional-transform
    error
    Whenschema.encodeAsync() is called on a schema containing a .transform() that has no inverse encode function. Zod v4 transforms created with .transform(fn) are unidirectional — they can decode (forward) but cannot encode (backward). Only schemas built with z.transform({ decode, encode }) bidirectional codecs support the encode direction.
    ThrowsZodEncodeError: Encountered unidirectional transform during encode: ZodTransform
    Required handlingOnly use encodeAsync() on schemas built with bidirectional codecs: // ❌ WRONG — .transform() is unidirectional const schema = z.string().transform(s => s.toUpperCase()); await schema.encodeAsync('HELLO'); // throws ZodEncodeError // ✅ CORRECT — z.transform with encode/decode pair const schema = z.transform({ decode: (s: string) => s.toUpperCase(), encode: (s: string) => s.toLowerCase(), }); await schema.encodeAsync('HELLO'); // works Catch blocks MUST handle ZodEncodeError separately from ZodError: try { await schema.encodeAsync(value); } catch (error) { if (error instanceof ZodEncodeError) { /* schema bug */ } if (error instanceof z.ZodError) { /* validation failure */ } }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][7]
  • encodeAsync · encode-async-validation-error
    error
    Whenschema.encodeAsync() is called with a value that fails the schema's output-side validation. The backward direction still runs validators — if the encoded value does not satisfy the schema, ZodError is thrown.
    ThrowsZodError with .issues array containing validation failures
    Required handlingCaller MUST catch both ZodEncodeError and ZodError: try { const encoded = await schema.encodeAsync(value); } catch (error) { if (error.name === 'ZodEncodeError') { // Schema design error — transform is not reversible throw new Error('Schema does not support encoding'); } if (error instanceof z.ZodError) { // Value failed validation during encode console.error(error.issues); } }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • decodeAsync · decode-async-validation-error
    error
    Whenschema.decodeAsync() is called with a value that fails the schema's type validation, constraints (min, max, regex, etc.), or synchronous refinements. The async parse path runs all validators and collects issues, then throws ZodError if any issues exist.
    ThrowsZodError with .issues array describing all validation failures
    Required handlingCaller MUST wrap decodeAsync() in try-catch: try { const result = await schema.decodeAsync(data); // Use result } catch (error) { if (error instanceof z.ZodError) { // Validation failed — inspect error.issues for (const issue of error.issues) { console.error(issue.path, issue.message); } } } Or use safeDecodeAsync() to get a non-throwing result object.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][8]
  • decodeAsync · decode-async-refinement-error
    error
    Whenschema.decodeAsync() is called on a schema with async refinements (.refine(async fn)) where the refinement rejects or returns false. The rejection is collected as a ZodIssue with code: 'custom' and thrown as a ZodError. Unhandled promise rejections inside async refinements crash the await if not caught by Zod.
    ThrowsZodError with issue code: 'custom' from the failed async refinement
    Required handlingWhen async refinements call external services (DB uniqueness checks, API calls), handle both the ZodError AND consider that the underlying async error has been absorbed: try { const result = await schema.decodeAsync(data); } catch (error) { if (error instanceof z.ZodError) { const customIssues = error.issues.filter(i => i.code === 'custom'); // customIssues may reflect DB errors, not just validation failures throw new ValidationError(customIssues); } }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][4]
  • safeEncodeAsync · safe-encode-async-unidirectional-transform
    error
    Whenschema.safeEncodeAsync() is called on a schema containing a .transform() with no inverse encode function. The "safe" prefix does NOT cover this case — schema._zod.run() throws $ZodEncodeError synchronously inside the async function, which propagates as a promise rejection.
    Throws$ZodEncodeError: Encountered unidirectional transform during encode: ZodTransform (promise rejection)
    Required handlingUse bidirectional codecs OR wrap safeEncodeAsync() in try-catch: // wrong (unidirectional transform — rejects) const result = await z.string().transform(s => s.toUpperCase()).safeEncodeAsync('HELLO'); // correct option 1: use bidirectional codec const schema = z.transform({ decode: (s: string) => s.toUpperCase(), encode: (s: string) => s.toLowerCase(), }); const result = await schema.safeEncodeAsync('HELLO'); // correct option 2: wrap in try-catch try { const result = await schema.safeEncodeAsync(value); if (!result.success) { /* validation issues */ } } catch (err) { // err.name === 'ZodEncodeError' — schema design bug }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][7]
  • safeEncodeAsync · safe-encode-async-refinement-throw
    warning
    Whenschema.safeEncodeAsync() is called on a schema whose async refinement throws an error (rather than returning false). The original error propagates as a promise rejection — not wrapped in ZodError, not a {success:false} result.
    ThrowsOriginal Error from the throwing async refinement body (not wrapped in ZodError)
    Required handlingWrap safeEncodeAsync() in try-catch when the schema has async refinements that may throw.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • safeDecodeAsync · safe-decode-async-refinement-throw
    warning
    Whenschema.safeDecodeAsync() is called on a schema whose async refinement (.refine(async fn)) THROWS an error rather than returning false. The throw is NOT caught by the "safe" wrapper — it propagates as a promise rejection with the original error type.
    ThrowsOriginal Error from the throwing async refinement body (not wrapped in ZodError)
    Required handlingWrap safeDecodeAsync() in try-catch when async refinements may throw: try { const result = await schema.safeDecodeAsync(data); if (!result.success) { /* handle ZodError */ } } catch (err) { // async refinement threw (DB error, network error, etc.) }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Official documentation
  • [4]
    zod.dev/api
    Api
  • [8]
    zod.dev/api
    Api
Source code

Research notes

Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.

Sources for zod Contract

Contract Version: 1.0.0 Last Verified: 2026-02-24


Official Documentation


CVE Analysis

CVE-2023-4316 - ReDoS in Email Validation

  • Severity: Medium (CVSS score not provided)
  • Affected Versions: zod 3.22.2 and earlier
  • Fixed In: zod 3.22.3+
  • Description: Regular Expression Denial of Service (ReDoS) vulnerability in email validation using insecure regex pattern
  • Impact: Attackers can cause denial of service by providing maliciously crafted email strings
  • Mitigation: Upgrade to zod >= 3.22.3

References:

CVE-2024-32866 - Prototype Pollution in @conform-to/zod

  • Note: This affects the @conform-to/zod integration package, not zod itself
  • Severity: High (CVSS 8.6)
  • Affected Versions: @conform-to/zod <= 1.1.0
  • Fixed In: @conform-to/zod 1.1.1 and 0.9.2
  • Not directly applicable to zod core library

Reference:


Source Code References

ZodError Class

  • src/ZodError.ts - Error class implementation
  • Error structure includes:
    • issues: ZodIssue[] - Array of validation failures
    • Each issue contains: code, path, message, expected, received

Parse Methods Implementation

  • src/types.ts - Core type implementations
  • parse() - Throws ZodError on failure
  • safeParse() - Returns discriminated union {success: boolean}
  • parseAsync() - Async parse with ZodError throwing
  • safeParseAsync() - Async with discriminated union

Common Error Codes

  • invalid_type - Expected type doesn't match received type
  • too_small - Value below minimum (strings, arrays, numbers)
  • too_big - Value above maximum
  • invalid_string - String-specific validation failures (email, url, uuid, etc.)
  • custom - Custom refinement failures
  • invalid_union - Union validation failures
  • invalid_date - Date coercion failures

Real-World Usage Analysis

jake-tennis-ai-collections Repository

Total zod imports found: 20+ files

Usage Patterns:

  1. Schema Definition - Most common pattern

    const formSchema = z.object({
      email: z.string().email(),
      password: z.string().min(7)
    });
    
  2. react-hook-form Integration

    import { zodResolver } from '@hookform/resolvers/zod';
    
    const form = useForm<z.infer<typeof formSchema>>({
      resolver: zodResolver(formSchema),
    });
    
    • Files: 15+ form components
    • Pattern: Never directly call parse(), let zodResolver handle it
  3. Custom Validation Helper (src/lib/validations.ts:409-422)

    export function validateData<T>(
      schema: z.ZodSchema<T>,
      data: unknown
    ): { success: true; data: T } | { success: false; error: z.ZodError } {
      try {
        const result = schema.parse(data);
        return { success: true, data: result };
      } catch (error) {
        if (error instanceof z.ZodError) {
          return { success: false, error };
        }
        throw error;
      }
    }
    
    • Anti-pattern detected: This helper mimics safeParse() - should just use safeParse() directly
    • Files using parse(): 11 files call .parse() directly
    • Files using safeParse(): 0 files (!)
  4. Complex Validation with Refinements

    const schema = z.object({...}).refine(
      (data) => data.amount_due >= data.amount_paid,
      { message: 'Amount due must be >= amount paid', path: ['amount_due'] }
    );
    
    • Files: validations.ts (multiple schemas)
    • Pattern: Heavy use of custom refinements for business logic
  5. Type Coercion

    z.coerce.date() // Convert string to Date
    
    • Files: src/features/customers/data/schema.ts:77

Key Findings:

  • Good: All schemas well-typed with TypeScript inference
  • Good: Extensive use of custom refinements for business validation
  • Anti-pattern: Using parse() wrapped in try-catch instead of safeParse()
  • Anti-pattern: No error handling for direct parse() calls
  • ℹ️ Note: react-hook-form integration handles all validation errors automatically

Community References

GitHub Issues

  • Issue #2828 - CVE-2023-4316 security vulnerability discussion
  • Common questions:
    • Error formatting and custom error messages
    • Async validation patterns
    • Integration with form libraries

Stack Overflow

  • Common questions about zod:
    • "How to handle ZodError?" - Most answered with safeParse() recommendation
    • "Async validation with zod" - Use parseAsync() or safeParseAsync()
    • "Custom error messages" - Use second parameter of refine() or custom errorMap

Integration Patterns

react-hook-form + zod

The most common integration in production apps:

import { zodResolver } from '@hookform/resolvers/zod';

const formSchema = z.object({ ... });

const form = useForm({
  resolver: zodResolver(formSchema),
});

Key behavior: zodResolver internally uses safeParse() and maps errors to form fields. Developers never directly handle ZodError in this pattern.


Nark profile Rationale

Why parse() and parseAsync() are contracted:

  1. Throws exceptions - Requires explicit error handling or crashes
  2. Common anti-pattern - Frequently used without try-catch in real codebases
  3. Silent failures - Unhandled ZodError can crash applications or servers
  4. Security implications - Invalid input should be handled gracefully (fail-safe)

Why safeParse() and safeParseAsync() are WARNING severity:

  1. Never throws - Returns discriminated union instead
  2. Type-safe - TypeScript enforces checking result.success
  3. Less critical - Still important to check success, but won't crash if forgotten
  4. Best practice - Recommended approach in documentation

Testing Strategy

Test Coverage Needed:

  1. parse() without try-catch → Should detect violation
  2. parseAsync() without try-catch → Should detect violation
  3. parse() with proper try-catch → Should NOT detect violation
  4. safeParse() usage → Should warn if success not checked (lower priority)
  5. Coercion failures → Should detect when z.coerce.* used with parse()
  6. Async refinements → Should detect when parse() used instead of parseAsync()

Contract Maintenance Notes

Version Coverage

  • Semver: >=3.0.0
  • Rationale: API has been stable since v3. parse() behavior consistent across all v3.x versions.
  • CVE Note: Recommend >= 3.22.3 to avoid ReDoS vulnerability

Future Considerations

  1. v4.x breaking changes - Monitor for API changes to parse methods
  2. New validation methods - Check if new methods throw exceptions
  3. Error structure changes - Monitor ZodError.issues format
  4. Performance improvements - Newer versions may have different performance characteristics

Related Contracts

  • react-hook-form - Often used together (zodResolver integration)
  • @conform-to/zod - Form validation integration (has separate CVE)

Notes

  • Zod is heavily influenced by Yup but has better TypeScript support
  • The library emphasizes type inference with z.infer<typeof schema>
  • Zod schemas are composable with .merge(), .extend(), .pick(), .omit()
  • The superRefine() method provides lower-level refinement control for complex validations
Need a different package?
Request a profile