colinhacks/zod · error · Error
Async schemas not supported in object keys currently
Error message
Async schemas not supported in object keys currently
What it means
Thrown during record parsing on the fast path that iterates def.keyType._zod.values, when running the key schema on a candidate key returns a Promise. Object keys must be validated synchronously because they are enumerated eagerly to build the output object; an async key schema (e.g. one containing z.string().refine(async ...) or a transform that returns a promise) cannot be awaited inline and is rejected. The check is `if (keyResult instanceof Promise)`.
Solutions
- Remove all async refinements/transforms from the record's key schema.
- Replace async validation with a synchronous approximation (regex, .refine without async, .regex).
- If async key validation is truly required, validate keys manually before construction instead of via the record's keyType.
Example fix
// before const R = z.record( z.string().refine(async (k) => await isAllowed(k)), z.number() ); // after const R = z.record( z.string().refine((k) => ALLOWED.has(k)), z.number() );
Defensive patterns
Strategy: type-guard
Validate before calling
function isSyncKeySchema(keyType: z.ZodType): boolean {
// Run the schema on a sample value and confirm it doesn't return a Promise
const r = (keyType as any)._zod.run({ value: "__probe__", issues: [] }, undefined);
return !(r instanceof Promise);
} Type guard
function isSyncKeySchema(keyType: z.ZodType): boolean {
const r = (keyType as any)._zod.run({ value: "__probe__", issues: [] }, undefined);
return !(r instanceof Promise);
} Prevention
- Never use async refinements/transforms on a record's key schema.
- Keep key schemas to sync primitives (z.string(), z.number(), z.literal(), z.enum()).
- Do async checks outside the record, then construct it from filtered input.
When it happens
Trigger: Defining z.record(keyType, valueType) where keyType is or contains an async refinement or transform — e.g. z.string().refine(async () => ...), z.string().transform(async ...), or a pipe that yields a promise. Parsing any input against such a record triggers the throw on the first value-key iteration.
Common situations: Using a database-backed validator on the key schema; copying a value-side refine (that happens to be async) onto the key side; piping the key through an async preprocess; mixing z.record() with schemas designed for async value validation.
Related errors
- Async refinement encountered during synchronous parse…
- Asynchronous transform encountered during synchronous parse…
- invalid_arguments
- invalid_return_type
- Map cannot be represented in JSON Schema
AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11).
Data as JSON: /api/errors/d67f720d670fe524.
Report an issue: GitHub.
Appendix: source
Thrown at packages/zod/src/v4/core/schemas.ts:2920
input,
inst,
});
return payload;
}
const proms: Promise<any>[] = [];
const values = def.keyType._zod.values;
if (values) {
payload.value = {};
const recordKeys = new Set<string | symbol>();
for (const key of values) {
if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
recordKeys.add(typeof key === "number" ? key.toString() : key);
const keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);
if (keyResult instanceof Promise) {
throw new Error("Async schemas not supported in object keys currently");
}
if (keyResult.issues.length) {
payload.issues.push({
code: "invalid_key",
origin: "record",
issues: keyResult.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())),
input: key,
path: [key],
inst,
});
continue;
}
const outKey = keyResult.value as PropertyKey;
const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);
if (result instanceof Promise) {
proms.push(
result.then((result) => {View on GitHub (pinned to 2d90846af9)