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

  1. Remove all async refinements/transforms from the record's key schema.
  2. Replace async validation with a synchronous approximation (regex, .refine without async, .regex).
  3. 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

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


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)