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
- Call `.parseAsync(data)` / `.spa(data)` instead of `.parse` / `.safeParse`.
- If you cannot go async, make the refinement synchronous (e.g. cache the lookup, or move the check out of the schema).
- 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
- Treat any refinement using `async` as forcing parseAsync everywhere the schema is used.
- Keep side-effecting (DB/HTTP) checks out of schemas used in sync paths.
- Annotate async-only schemas with a JSDoc note.
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
- Asynchronous transform encountered during synchronous parse
- Synchronous parse encountered promise.
- Can't use "invalid_type_error" or "required_error" in conjun
- A discriminator value for key `${discriminator}` could not b
- Discriminator property ${String(discriminator)} has duplicat
AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03).
Data as JSON: /data/errors/935607010faa81ec.json.
Report an issue: GitHub.