colinhacks/zod · error · Error

Asynchronous transform encountered during synchronous parse…

Error message

Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.

What it means

Thrown at packages/zod/src/v3/types.ts:4421 during a synchronous parse when a transform's return value is a Promise. In the sync branch of the transform effect, Zod checks `if (result instanceof Promise)` and throws, because it cannot await the result inline. The message points to the remedy: use .parseAsync().

Solutions

  1. Move the call site to `await schema.parseAsync(data)` / `await schema.safeParseAsync(data)`.
  2. If the call site must stay sync, replace the async transform with a synchronous one (do the I/O before parse, pass the result in).
  3. Restructure so the async work happens outside the schema and the transform only reshapes already-fetched data synchronously.
  4. Audit every .transform() on the schema for a returned Promise.

Example fix

// before
const schema = z
  .string()
  .transform(async (id) => await db.find(id));
schema.parse(input); // throws: Asynchronous transform encountered...

// after
await schema.parseAsync(input);
Defensive patterns

Strategy: validation

Validate before calling

import { ZodType, ZodFirstPartyTypeKind } from 'zod';

function findAsyncTransforms(schema: ZodType, path: string[] = []): string[] {
  const def = (schema as any)._def;
  if (!def) return [];
  if (def.typeName === ZodFirstPartyTypeKind.ZodEffects && def.effect?.type === 'transform') {
    const src = def.effect.transform?.toString() ?? '';
    if (/\basync\b/.test(src)) return [path.join('.') || '(root)'];
    return findAsyncTransforms(def.schema, path);
  }
  return [];
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Defining `z.string().transform(async (v) => await fetchDetails(v))` and parsing through `.parse()` or `.safeParse()` (sync). The transform returns a Promise, triggering the guard.

Common situations: Writing a transform that does I/O (fetch, DB, file read) and forgetting that transforms default to the sync parse path; converting a preprocess/transform pipeline to async without updating callers; library code that calls .parse() on user schemas that may contain async transforms.

Related errors


AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11). Data as JSON: /api/errors/fef3ed001efd6eb4. Report an issue: GitHub.

Appendix: source

Thrown at packages/zod/src/v3/types.ts:4421

            return { status: status.value, value: inner.value };
          });
        });
      }
    }

    if (effect.type === "transform") {
      if (ctx.common.async === false) {
        const base = this._def.schema._parseSync({
          data: ctx.data,
          path: ctx.path,
          parent: ctx,
        });

        if (!isValid(base)) return INVALID;

        const result = effect.transform(base.value, checkCtx);
        if (result instanceof Promise) {
          throw new Error(
            `Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`
          );
        }

        return { status: status.value, value: result };
      } else {
        return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
          if (!isValid(base)) return INVALID;

          return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
            status: status.value,
            value: result,
          }));
        });
      }
    }

    util.assertNever(effect);

View on GitHub (pinned to 2d90846af9)