colinhacks/zod · error · Error

Invalid UUID version

Error message

Invalid UUID version: "${def.version}"

What it means

Thrown by the $ZodUUID constructor when def.version is set to a value not present in its internal versionMap. The map only accepts the string keys "v1" through "v8" (mapped to integers 1-8), so any other string (e.g. "v9", "v10", "uuid", "4") is rejected at schema-construction time. The version is used to select the corresponding UUID regex from regexes.uuid(v).

Solutions

  1. Use one of the supported version strings: "v1", "v2", "v3", "v4", "v5", "v6", "v7", or "v8".
  2. If you do not need a specific version, omit the version option entirely to accept any UUID format.
  3. If your zod version predates v6/v7/v8 support, upgrade zod to a release that includes them.
  4. Validate or sanitize user-supplied version strings against the allowed set before passing them to z.uuid().

Example fix

// before
const Id = z.uuid({ version: "v9" });
// after
const Id = z.uuid({ version: "v7" });
// or accept any version
const Id = z.uuid();
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = new Set(["v1","v2","v3","v4","v5","v6","v7","v8"]);
function makeUuid(version?: string) {
  if (version !== undefined && !ALLOWED.has(version)) {
    throw new Error(`Unsupported UUID version: ${version}. Allowed: ${[...ALLOWED].join(", ")}`);
  }
  return version ? z.uuid({ version }) : z.uuid();
}

Type guard

function isValidUuidVersion(v: unknown): v is `v${1|2|3|4|5|6|7|8}` {
  return typeof v === "string" && /^v[1-8]$/.test(v);
}

Try / catch

try {
  const Id = makeUuid(config.version);
} catch (e) {
  // surface a config-level error to the user, with the allowed list
  throw new Error(`Bad config.version: ${(e as Error).message}`);
}

Prevention

When it happens

Trigger: Calling z.uuid({ version: "v9" }), z.uuid({ version: "9" }), z.uuid({ version: "uuid" }), or constructing $ZodUUID with a def.version outside the v1-v8 set. Any non-empty version string that isn't exactly "v1".."v8" triggers it; omitting version (or passing undefined) skips the check entirely and falls back to the generic UUID regex.

Common situations: Developers assume the version takes a bare number ("4") instead of the "v4" prefix; copying version strings from docs of other libraries; auto-generating UUID config from user input without validation; trying to use newer UUID variants (v6/v7/v8 were added later — older zod versions only supported v1-v5).

Related errors


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

Appendix: source

Thrown at packages/zod/src/v4/core/schemas.ts:443

export interface $ZodUUID extends $ZodType {
  _zod: $ZodUUIDInternals;
}

export const $ZodUUID: core.$constructor<$ZodUUID> = /*@__PURE__*/ core.$constructor("$ZodUUID", (inst, def): void => {
  if (def.version) {
    const versionMap: Record<string, number> = {
      v1: 1,
      v2: 2,
      v3: 3,
      v4: 4,
      v5: 5,
      v6: 6,
      v7: 7,
      v8: 8,
    };
    const v = versionMap[def.version];
    if (v === undefined) throw new Error(`Invalid UUID version: "${def.version}"`);
    def.pattern ??= regexes.uuid(v);
  } else def.pattern ??= regexes.uuid();
  $ZodStringFormat.init(inst, def);
});

//////////////////////////////   ZodEmail   //////////////////////////////

export interface $ZodEmailDef extends $ZodStringFormatDef<"email"> {}
export interface $ZodEmailInternals extends $ZodStringFormatInternals<"email"> {}
export interface $ZodEmail extends $ZodType {
  _zod: $ZodEmailInternals;
}

export const $ZodEmail: core.$constructor<$ZodEmail> = /*@__PURE__*/ core.$constructor(
  "$ZodEmail",
  (inst, def): void => {
    def.pattern ??= regexes.email;
    $ZodStringFormat.init(inst, def);

View on GitHub (pinned to 2d90846af9)