colinhacks/zod · error · Error

Invalid template literal part

Error message

Invalid template literal part: ${part._zod.traits}

What it means

Thrown by $ZodTemplateLiteral when a part is a schema object (has _zod) and has a _zod.pattern key, but the extracted `source` is falsy after coercion — i.e. part._zod.pattern is an empty string, null, or 0. The constructor already passed the existence check on line 4313, but the resolved source string is empty, so there is no regex fragment to concatenate. The message dumps the part's full traits set for diagnosis.

Solutions

  1. Ensure the schema's _zod.pattern resolves to a non-empty regex/string (e.g. new RegExp(".+")).
  2. If using a custom schema, set inst._zod.pattern = /some-source/; in its constructor.
  3. Replace the offending part with a known-patterned schema like z.string() or z.literal().
  4. Debug by logging part._zod.pattern before constructing the template to find which part is empty.

Example fix

// before — custom schema with empty pattern
const Empty = z.custom(/* ... */);
Empty._zod.pattern = "";
const T = z.template`${Empty}`;
// after
Empty._zod.pattern = /.+/;
const T = z.template`${Empty}`;
Defensive patterns

Strategy: validation

Validate before calling

function assertNonEmptyPattern(part: unknown) {
  const p = (part as any)?._zod?.pattern;
  const source = p instanceof RegExp ? p.source : p;
  if (!source) throw new Error("Template part has empty pattern source.");
}

Type guard

function hasNonEmptyPattern(part: unknown): boolean {
  const p = (part as any)?._zod?.pattern;
  const source = p instanceof RegExp ? p.source : p;
  return typeof source === "string" && source.length > 0;
}

Prevention

When it happens

Trigger: A custom schema that sets inst._zod.pattern = "" explicitly; a schema whose pattern getter returns an empty string under some condition; a wrapper that strips the pattern source; rare edge cases where a patterned schema yields an empty source after the startsWith("^")/endsWith("$") slicing.

Common situations: Custom schema authors assigning an empty pattern by mistake; schemas derived from a base whose pattern was deleted; middleware that clears _zod.pattern; edge cases in third-party schema plugins.

Related errors


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

Appendix: source

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

      : never
    : never;

export const $ZodTemplateLiteral: core.$constructor<$ZodTemplateLiteral> = /*@__PURE__*/ core.$constructor(
  "$ZodTemplateLiteral",
  (inst, def) => {
    $ZodType.init(inst, def);
    const regexParts: string[] = [];
    for (const part of def.parts) {
      if (typeof part === "object" && part !== null) {
        // is Zod schema
        if (!part._zod.pattern) {
          // if (!source)
          throw new Error(`Invalid template literal part, no pattern found: ${[...(part as any)._zod.traits].shift()}`);
        }

        const source = part._zod.pattern instanceof RegExp ? part._zod.pattern.source : part._zod.pattern;

        if (!source) throw new Error(`Invalid template literal part: ${part._zod.traits}`);

        const start = source.startsWith("^") ? 1 : 0;
        const end = source.endsWith("$") ? source.length - 1 : source.length;
        regexParts.push(source.slice(start, end));
      } else if (part === null || util.primitiveTypes.has(typeof part)) {
        regexParts.push(util.escapeRegex(`${part}`));
      } else {
        throw new Error(`Invalid template literal part: ${part}`);
      }
    }
    inst._zod.pattern = new RegExp(`^${regexParts.join("")}$`);

    inst._zod.parse = (payload, _ctx) => {
      if (typeof payload.value !== "string") {
        payload.issues.push({
          input: payload.value,
          inst,
          expected: "string",

View on GitHub (pinned to 2d90846af9)