colinhacks/zod · error · Error

Cannot mix number and bigint in multiple_of check.

Error message

Cannot mix number and bigint in multiple_of check.

What it means

The `multiple_of` check (checks.ts:168) computes divisibility differently for numbers (float-safe remainder) vs bigints (native `%`). To pick the right branch it asserts `typeof payload.value === typeof def.value` at checks.ts:177 and throws if they differ. This is a parse-time throw, not construction-time: it fires when the runtime input type does not match the divisor's type.

Source

Thrown at packages/zod/src/v4/core/checks.ts:178

  def: $ZodCheckMultipleOfDef<T>;
  issc: errors.$ZodIssueNotMultipleOf;
}

export interface $ZodCheckMultipleOf<T extends number | bigint = number | bigint> extends $ZodCheck<T> {
  _zod: $ZodCheckMultipleOfInternals<T>;
}

export const $ZodCheckMultipleOf: core.$constructor<$ZodCheckMultipleOf<number | bigint>> =
  /*@__PURE__*/ core.$constructor("$ZodCheckMultipleOf", (inst, def) => {
    $ZodCheck.init(inst, def);

    inst._zod.onattach.push((inst) => {
      inst._zod.bag.multipleOf ??= def.value;
    });

    inst._zod.check = (payload) => {
      if (typeof payload.value !== typeof def.value)
        throw new Error("Cannot mix number and bigint in multiple_of check.");
      const isMultiple =
        typeof payload.value === "bigint"
          ? payload.value % (def.value as bigint) === BigInt(0)
          : util.floatSafeRemainder(payload.value, def.value as number) === 0;

      if (isMultiple) return;
      payload.issues.push({
        origin: typeof payload.value as "number",
        code: "not_multiple_of",
        divisor: def.value as number,
        input: payload.value,
        inst,
        continue: !def.abort,
      });
    };
  });

/////////////////////////////////////

View on GitHub (pinned to 912f0f51b0)

Solutions

  1. Match the divisor type to the schema: `z.bigint().multipleOf(BigInt(5))` for bigint, `z.number().multipleOf(5)` for number.
  2. Normalize the input to the expected type before parsing (e.g. `BigInt(x)` for bigint schemas).
  3. If inputs are genuinely mixed, split into two schemas and branch on `typeof` before parsing.

Example fix

// before
const schema = z.bigint().multipleOf(5); // 5 is number -> throws on parse
schema.parse(BigInt(10));
// after
const schema = z.bigint().multipleOf(BigInt(5));
schema.parse(BigInt(10));
Defensive patterns

Strategy: validation

Validate before calling

// Ensure divisor type matches schema type before constructing.
function bigintMultipleOf(divisor: bigint) {
  return z.bigint().multipleOf(divisor); // divisor MUST be bigint
}
function numberMultipleOf(divisor: number) {
  return z.number().multipleOf(divisor); // divisor MUST be number
}

Type guard

function isBigIntSchemaDivisor(v: unknown): v is bigint {
  return typeof v === "bigint";
}
// before building: ensure typeof(schema input) === typeof(divisor)

Try / catch

try {
  schema.parse(input);
} catch (e) {
  if (e instanceof Error && /Cannot mix number and bigint/.test(e.message)) {
    // normalize input type and retry, or report a typed error
  }
  throw e;
}

Prevention

When it happens

Trigger: Defining `z.bigint().multipleOf(5)` (5 is a plain number) then `.parse(BigInt(10))`; OR `z.number().multipleOf(BigInt(5))` then `.parse(10)`. Also reachable by feeding a bigint into a number schema with a numeric divisor or vice versa via `.safeParse` on mixed data.

Common situations: Forgetting the `BigInt()` wrapper on the divisor when building a bigint schema; deserializing JSON (which has no bigint) into a bigint schema; a code path that sometimes receives a number and sometimes a bigint; refactoring a number schema to bigint without updating the divisor.

Related errors


AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03). Data as JSON: /data/errors/28f41809f2cc6506.json. Report an issue: GitHub.