colinhacks/zod · error · Error

Key not found in enum

Error message

Key ${value} not found in enum

What it means

Thrown by ZodEnum.prototype.extract(values) when one of the requested keys is not present in the enum's entries map. extract() builds a new enum containing only the listed keys, so every supplied value must already be an existing enum key.

Solutions

  1. Ensure every string passed to extract() is a literal key of the original enum; check the enum's .enum or .options first.
  2. If filtering by value rather than key, map values back to keys or use z.enum on the value array directly.
  3. Filter the requested list through Object.keys(enum.entries) before calling extract() so unknown keys are dropped instead of throwing.

Example fix

// before (throws — 'PURPLE' is not a key)
const Colors = z.enum(['RED', 'GREEN', 'BLUE']);
const Warm = Colors.extract(['RED', 'PURPLE']);

// after
const Warm = Colors.extract(['RED', 'BLUE']);
Defensive patterns

Strategy: validation

Validate before calling

function safeExtract(enumSchema, keys) {
  const valid = new Set(Object.keys(enumSchema.enum));
  const unknown = keys.filter((k) => !valid.has(k));
  if (unknown.length) {
    throw new Error(`Unknown enum keys: ${unknown.join(', ')}`);
  }
  return enumSchema.extract(keys);
}

Type guard

function isEnumKey(enumSchema, key) {
  return Object.prototype.hasOwnProperty.call(enumSchema.enum, key);
}

Try / catch

try {
  const Sub = MyEnum.extract(requestedKeys);
} catch (e) {
  if (e.message.includes('not found in enum')) {
    // log the offending key and degrade gracefully
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling myEnum.extract(['RED','PURPLE']) when 'PURPLE' was never defined. Passing the enum *values* instead of the enum *keys* to extract(). Renaming a key in the source enum but forgetting to update an extract() call elsewhere.

Common situations: Refactoring an enum and leaving stale extract() call sites. Confusing key vs value semantics (in z.enum they coincide for string enums, but native enums with numeric values or renamed keys diverge). Splitting a shared enum into sub-enums at multiple call sites.

Related errors


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

Appendix: source

Thrown at packages/zod/src/v4/classic/schemas.ts:1946

    params?: string | core.$ZodEnumParams
  ): ZodEnum<util.Flatten<Omit<T, U[number]>>>;
}
export const ZodEnum: core.$constructor<ZodEnum> = /*@__PURE__*/ core.$constructor("ZodEnum", (inst, def) => {
  core.$ZodEnum.init(inst, def);
  ZodType.init(inst, def);
  inst._zod.processJSONSchema = (ctx, json, params) => processors.enumProcessor(inst, ctx, json, params);

  inst.enum = def.entries;
  inst.options = Object.values(def.entries);

  const keys = new Set(Object.keys(def.entries));

  inst.extract = (values, params) => {
    const newEntries: Record<string, any> = {};
    for (const value of values) {
      if (keys.has(value)) {
        newEntries[value] = def.entries[value];
      } else throw new Error(`Key ${value} not found in enum`);
    }
    return new ZodEnum({
      ...def,
      checks: [],
      ...util.normalizeParams(params),
      entries: newEntries,
    }) as any;
  };

  inst.exclude = (values, params) => {
    const newEntries: Record<string, any> = { ...def.entries };
    for (const value of values) {
      if (keys.has(value)) {
        delete newEntries[value];
      } else throw new Error(`Key ${value} not found in enum`);
    }
    return new ZodEnum({
      ...def,

View on GitHub (pinned to 2d90846af9)