zod
semver
>=3.0.0postconditions15functions8last verified2026-06-24coverage score100%Postconditions: what we check
- parse · parse-validation-errorerrorWhenWhen input data does not match the schema definitionThrows
ZodErrorRequired 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 unavailablevisibilityvisibleSources[1] - parse · parse-type-coercion-errorerrorWhenWhen type coercion fails (e.g., z.coerce.date() receives invalid date string)Throws
ZodErrorRequired handlingCaller MUST handle coercion failures. Use safeParse() or try-catch.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - parse · parse-async-schema-errorerrorWhenschema.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 - parseAsync · parse-async-validation-errorerrorWhenWhen input data does not match the schema definition or async refinements failThrows
ZodErrorRequired handlingCaller MUST wrap parseAsync() in try-catch block or use safeParseAsync() instead. Handle rejected promises appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - parseAsync · parse-async-refinement-errorerrorWhenWhen custom async refinement validation failsThrows
ZodErrorRequired handlingCaller MUST handle async refinement failures. The error.issues array will contain details about which refinements failed.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - safeParse · safe-parse-success-checkwarningWhenWhen validation completes (success or failure)Throws
neverRequired handlingCaller MUST check result.success before accessing result.data or result.error. TypeScript discriminated unions enforce this at compile time.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - safeParseAsync · safe-parse-async-success-checkwarningWhenWhen async validation completes (success or failure)Throws
neverRequired handlingCaller MUST check result.success before accessing result.data or result.error. Handle the promise appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - safeParseAsync · safe-parse-async-refinement-throwwarningWhenschema.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).Throws
Original 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 unavailablevisibilityvisibleSources[3] - encodeAsync · encode-async-unidirectional-transformerrorWhenschema.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.Throws
ZodEncodeError: Encountered unidirectional transform during encode: ZodTransformRequired 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 - encodeAsync · encode-async-validation-errorerrorWhenschema.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.Throws
ZodError with .issues array containing validation failuresRequired 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 unavailablevisibilityvisibleSources[3] - decodeAsync · decode-async-validation-errorerrorWhenschema.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.Throws
ZodError with .issues array describing all validation failuresRequired 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 - decodeAsync · decode-async-refinement-errorerrorWhenschema.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.Throws
ZodError with issue code: 'custom' from the failed async refinementRequired 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 - safeEncodeAsync · safe-encode-async-unidirectional-transformerrorWhenschema.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 - safeEncodeAsync · safe-encode-async-refinement-throwwarningWhenschema.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.Throws
Original 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 unavailablevisibilityvisibleSources[3] - safeDecodeAsync · safe-decode-async-refinement-throwwarningWhenschema.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.Throws
Original 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 unavailablevisibilityvisibleSources[3]
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
Source code
- [1]github.com/colinhacks/zodcolinhacks/zod
- [2]github.com/colinhacks/zodcolinhacks/zod
- [3]raw.githubusercontent.com/colinhacks/zod/maincolinhacks/zod · parse.ts
- [5]github.com/colinhacks/zodcolinhacks/zod
- [6]github.com/colinhacks/zodcolinhacks/zod
- [7]raw.githubusercontent.com/colinhacks/zod/maincolinhacks/zod · schemas.ts
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
- Zod GitHub Repository - Main repository with comprehensive README
- Zod Official Website - Official documentation site
- Parse Methods - Documentation on parse() vs safeParse()
- Async Validation - parseAsync() and safeParseAsync() documentation
- Error Handling - ZodError structure and handling
- Refinements - Custom validation with .refine()
- Type Coercion - z.coerce.* 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:
- GHSA-m95q-7qp3-xv42 - GitHub Security Advisory
- Snyk Vulnerability - Snyk advisory
- GitHub Issue #2828 - Discussion thread
- Fluid Attacks Advisory - Detailed analysis
- fast-check Blog Post - How the vulnerability was discovered
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 failuresafeParse()- Returns discriminated union{success: boolean}parseAsync()- Async parse with ZodError throwingsafeParseAsync()- Async with discriminated union
Common Error Codes
invalid_type- Expected type doesn't match received typetoo_small- Value below minimum (strings, arrays, numbers)too_big- Value above maximuminvalid_string- String-specific validation failures (email, url, uuid, etc.)custom- Custom refinement failuresinvalid_union- Union validation failuresinvalid_date- Date coercion failures
Real-World Usage Analysis
jake-tennis-ai-collections Repository
Total zod imports found: 20+ files
Usage Patterns:
-
Schema Definition - Most common pattern
const formSchema = z.object({ email: z.string().email(), password: z.string().min(7) }); -
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
-
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 (!)
-
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
-
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:
- Throws exceptions - Requires explicit error handling or crashes
- Common anti-pattern - Frequently used without try-catch in real codebases
- Silent failures - Unhandled ZodError can crash applications or servers
- Security implications - Invalid input should be handled gracefully (fail-safe)
Why safeParse() and safeParseAsync() are WARNING severity:
- Never throws - Returns discriminated union instead
- Type-safe - TypeScript enforces checking result.success
- Less critical - Still important to check success, but won't crash if forgotten
- Best practice - Recommended approach in documentation
Testing Strategy
Test Coverage Needed:
- ✅ parse() without try-catch → Should detect violation
- ✅ parseAsync() without try-catch → Should detect violation
- ✅ parse() with proper try-catch → Should NOT detect violation
- ✅ safeParse() usage → Should warn if success not checked (lower priority)
- ✅ Coercion failures → Should detect when z.coerce.* used with parse()
- ✅ 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
- v4.x breaking changes - Monitor for API changes to parse methods
- New validation methods - Check if new methods throw exceptions
- Error structure changes - Monitor ZodError.issues format
- 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