colinhacks/zod · error · Error

Async refinement encountered during synchronous parse operat

Error message

Async refinement encountered during synchronous parse operation. Use .parseAsync instead.

What it means

Thrown inside ZodEffects when an effect of type 'refinement' returns a Promise during a synchronous parse (ctx.common.async === false). Refinements must be sync for sync parse; this guard prevents the parse from silently discarding an unawaited async check.

Source

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

        const result = this._def.schema._parseSync({
          data: processed,
          path: ctx.path,
          parent: ctx,
        });
        if (result.status === "aborted") return INVALID;
        if (result.status === "dirty") return DIRTY(result.value);
        if (status.value === "dirty") return DIRTY(result.value);
        return result;
      }
    }
    if (effect.type === "refinement") {
      const executeRefinement = (acc: unknown): any => {
        const result = effect.refinement(acc, checkCtx);
        if (ctx.common.async) {
          return Promise.resolve(result);
        }
        if (result instanceof Promise) {
          throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");
        }
        return acc;
      };

      if (ctx.common.async === false) {
        const inner = this._def.schema._parseSync({
          data: ctx.data,
          path: ctx.path,
          parent: ctx,
        });
        if (inner.status === "aborted") return INVALID;
        if (inner.status === "dirty") status.dirty();

        // return value is ignored
        executeRefinement(inner.value);
        return { status: status.value, value: inner.value };
      } else {
        return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {

View on GitHub (pinned to 912f0f51b0)

Solutions

  1. Call `.parseAsync(data)` / `.spa(data)` instead of `.parse` / `.safeParse`.
  2. If you cannot go async, make the refinement synchronous (e.g. cache the lookup, or move the check out of the schema).
  3. Move the async refinement into a separate code path so the sync schema stays sync.

Example fix

// before
const schema = z.object({ email: z.string() }).refine(
  async (v) => await isUnique(v.email)
);
schema.parse(input); // throws

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

Strategy: retry

Validate before calling

function isRefinementAsync(fn: Function) {
  // best-effort: parse a probe and detect a returned Promise
  const r = fn(undefined as any, undefined as any);
  return r instanceof Promise;
}

Try / catch

try { schema.parse(data); }
catch (e) {
  if (e instanceof Error && /Async refinement|Use \.parseAsync/.test(e.message)) {
    return await schema.parseAsync(data);
  }
  throw e;
}

Prevention

When it happens

Trigger: Defining `.refine(async (val) => ...)` and then calling `.parse()` or `.safeParse()` (not the Async variants). Also triggered when a sync schema embeds an async-refined subschema.

Common situations: Adding an async side-effecting validation (DB lookup, HTTP check) to a schema used in a sync request handler; tests calling `.parse` on an async schema; refactors that turn a sync check async.

Related errors


AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03). Data as JSON: /data/errors/935607010faa81ec.json. Report an issue: GitHub.