This document covers the codec system in Zod, which enables bidirectional transformations between two schema types. Codecs provide a way to convert data in both directions: from an input representation to an output representation (decode), and from output back to input (encode). This is particularly useful for serialization boundaries such as network communication, where data must be converted between a wire format (e.g., JSON strings) and a richer in-memory representation (e.g., Date objects).
For unidirectional transformations, see Transformations and Preprocessing. For information on default value handling, see Default and Prefault Values.
Sources: packages/docs/content/codecs.mdx1-50
A codec encapsulates a transformation between two schema types with operations in both directions:
Diagram: Bidirectional transformation flow in codecs
The codec maintains two schemas and two transformation functions that must be inverse operations of each other.
Sources: packages/docs/content/codecs.mdx49-86
The validation pipeline processes data differently based on direction, controlled by the direction field in ParseContextInternal:
| Operation | Direction | Schema Sequence | Type Safety |
|---|---|---|---|
.parse() | "forward" | Input → Output | Accepts unknown |
.decode() | "forward" | Input → Output | Strongly typed input |
.encode() | "backward" | Output → Input | Strongly typed input |
The direction affects how validation proceeds through the schema pipeline and which checks are executed.
Sources: packages/zod/src/v4/core/schemas.ts28-32 packages/docs/content/codecs.mdx111-135
Diagram: Type signatures for parsing, decoding, and encoding operations
The .parse() method accepts unknown input for maximum flexibility, while .decode() and .encode() have strongly-typed inputs to surface compile-time errors when transforming already-typed data.
Sources: packages/docs/content/codecs.mdx112-135 packages/zod/src/v4/classic/schemas.ts57-89
The z.codec() function creates a bidirectional schema:
Both decode and encode functions receive a refinement context for issuing validation errors during transformation.
Sources: packages/zod/src/v4/classic/tests/codec-examples.test.ts8-12
Classic API provides multiple methods for working with codecs:
| Method | Sync | Type | Returns |
|---|---|---|---|
.parse(data) | ✓ | Throws | Output |
.decode(data) | ✓ | Throws | Output |
.encode(data) | ✓ | Throws | Input |
.safeParse(data) | ✓ | Safe | SafeParseResult<Output> |
.safeDecode(data) | ✓ | Safe | SafeParseResult<Output> |
.safeEncode(data) | ✓ | Safe | SafeParseResult<Input> |
.parseAsync(data) | ✗ | Throws | Promise<Output> |
.decodeAsync(data) | ✗ | Throws | Promise<Output> |
.encodeAsync(data) | ✗ | Throws | Promise<Input> |
.safeParseAsync(data) | ✗ | Safe | Promise<SafeParseResult<Output>> |
.safeDecodeAsync(data) | ✗ | Safe | Promise<SafeParseResult<Output>> |
.safeEncodeAsync(data) | ✗ | Safe | Promise<SafeParseResult<Input>> |
Sources: packages/zod/src/v4/classic/schemas.ts57-89 packages/docs/content/codecs.mdx147-161
When encoding (backward direction), the validation system performs two passes to ensure type safety:
Diagram: Two-pass validation during encode operations
This two-pass approach prevents unexpected errors in refinement logic by ensuring the input conforms to the expected type before running any checks or transformations.
Sources: packages/zod/src/v4/core/schemas.ts273-300 packages/docs/content/codecs.mdx196-228
The run method in $ZodType handles directional logic:
Diagram: Direction-aware validation flow in the run method
The backward direction requires a canary pass to verify type conformance before executing checks, while the forward direction runs checks after parsing.
Sources: packages/zod/src/v4/core/schemas.ts273-300
The ParsePayload includes an aborted flag specifically for codec and pipe operations:
This flag allows early termination of validation chains when a transformation fails, preventing unnecessary downstream processing.
Sources: packages/zod/src/v4/core/schemas.ts34-39
Codecs maintain distinct input and output types accessible through Zod's type inference utilities:
The type system ensures that decode() operations accept the input type and return the output type, while encode() operations work in reverse.
Sources: packages/docs/content/codecs.mdx48-86
The $ZodTypeInternals interface stores type information:
Diagram: Internal type information storage in schema instances
The generic parameters O (output) and I (input) flow through the entire schema hierarchy, enabling precise type inference for codec operations.
Sources: packages/zod/src/v4/core/schemas.ts163-168
Codecs integrate seamlessly into object schemas, arrays, and other composite types:
Diagram: Codec composition within object schemas
When encoding an object containing codecs, each field's codec transforms its value in the backward direction. When decoding, the transformations run forward.
Sources: packages/docs/content/codecs.mdx96-109
Internally, codecs are implemented as a specialized subclass of ZodPipe with interstitial transform logic:
Diagram: Codec as specialized pipe with bidirectional semantics
During encoding, a pipe reverses its validation sequence: data is first encoded through the second schema, then passed back through the first schema.
Sources: packages/docs/content/codecs.mdx188-194
Sources: packages/zod/src/v4/classic/tests/codec-examples.test.ts8-60
Sources: packages/zod/src/v4/classic/tests/codec-examples.test.ts66-118
Diagram: Common date/time codec patterns
Sources: packages/zod/src/v4/classic/tests/codec-examples.test.ts124-146
Sources: packages/zod/src/v4/classic/tests/codec-examples.test.ts151-202
The JSON codec is a higher-order function that accepts an output schema:
The refinement context allows codecs to report structured errors when transformation fails.
Sources: packages/zod/src/v4/classic/tests/codec-examples.test.ts208-242
Diagram: Binary encoding codec patterns
Sources: packages/zod/src/v4/classic/tests/codec-examples.test.ts248-387
Both decode and encode functions can be asynchronous:
When using async transformations, the corresponding async methods must be used:
.decodeAsync() instead of .decode().encodeAsync() instead of .encode().safeDecodeAsync() instead of .safeDecode().safeEncodeAsync() instead of .safeEncode()Sources: packages/docs/content/codecs.mdx137-161
The parse pipeline detects async operations and handles them appropriately:
Diagram: Async detection and handling in validation pipeline
If an async operation is encountered during synchronous parsing (when ctx.async === false), a $ZodAsyncError is thrown to prevent incorrect behavior.
Sources: packages/zod/src/v4/core/schemas.ts230-247
All refinements (.refine(), .min(), .max(), etc.) execute in both directions:
The two-pass validation during encoding ensures refinements don't encounter type mismatches.
Sources: packages/docs/content/codecs.mdx196-228
Default and prefault values apply only in the forward direction:
Since defaults make the input type optional (T | undefined) but not the output type, undefined is not a valid input to encode().
Sources: packages/docs/content/codecs.mdx229-243
Similarly, .catch() only applies in the forward direction:
Sources: packages/docs/content/codecs.mdx244-257
If a schema contains any .transform() operations, attempting to encode will throw a runtime error (not a ZodError):
This prevents ambiguous behavior since transforms are inherently unidirectional and cannot be automatically reversed.
Sources: packages/docs/content/codecs.mdx284-293
Codecs enable a shared schema approach for API boundaries:
Diagram: Codec usage pattern across network boundaries
Both client and server import the same codec definition, using decode() on the receiving end and encode() on the sending end.
Sources: packages/docs/content/codecs.mdx85-93
Codecs implement the Standard Schema jsonSchema property for documentation generation:
This allows tools to generate OpenAPI specifications or JSON Schema documentation for APIs that use codecs.
Sources: packages/zod/src/v4/classic/schemas.ts19 packages/zod/src/v4/core/schemas.ts170-181
The ParseContextInternal extends the public ParseContext with internal fields:
The direction field controls whether validation proceeds forward (decode) or backward (encode), while skipChecks enables the canary pass during encoding.
Sources: packages/zod/src/v4/core/schemas.ts28-32
The check execution system in $ZodType initialization handles aborted payloads:
Checks can be skipped if the payload is marked as aborted, preventing unnecessary computation after a codec transformation fails.
Sources: packages/zod/src/v4/core/schemas.ts213-256
The backward direction handling in the run method implements the canary pass:
The canary pass runs parsing without checks to verify the type matches expectations before executing refinements and transformations.
Refresh this wiki