colinhacks/zod · error · Error

Invalid hex string length

Error message

Invalid hex string length

What it means

Thrown by hexToUint8Array() when the input hex string has an odd number of characters (after stripping an optional `0x` prefix). Each byte is two hex digits, so an odd-length string cannot be split into complete byte pairs. Used by codec helpers that back the `hex`/base64 string formats and any caller converting hex to bytes.

Solutions

  1. Pad the hex string to an even length with a leading zero: `hex.length % 2 ? '0' + hex : hex`.
  2. Strip whitespace and any non-hex characters before conversion, then re-check length.
  3. Validate the source format upstream (fixed-width hex columns, zero-padded input fields).

Example fix

// before
util.hexToUint8Array('0x4A3'); // throws: odd length

// after
const clean = '0x4A3'.replace(/^0x/, '');
const padded = clean.length % 2 ? '0' + clean : clean;
util.hexToUint8Array(padded);
Defensive patterns

Strategy: validation

Validate before calling

function toEvenHex(input: string): string {
  const clean = input.trim().replace(/^0x/i, '');
  if (!/^[0-9a-fA-F]*$/.test(clean)) {
    throw new TypeError('Input is not a valid hex string');
  }
  return clean.length % 2 ? '0' + clean : clean;
}
// util.hexToUint8Array(toEvenHex(userHex));

Type guard

function isValidHex(input: string): boolean {
  const clean = input.trim().replace(/^0x/i, '');
  return /^[0-9a-fA-F]*$/.test(clean) && clean.length % 2 === 0 && clean.length > 0;
}

Prevention

When it happens

Trigger: Passing a malformed hex value like `"0x4A3"`, `"abc"`, or a string that lost a leading zero (e.g. `"0FF"` instead of `"00FF"`). Also hit when user input or a DB column stores hex without zero-padding single-digit bytes.

Common situations: Storing hashes/IDs as hex without fixed-width padding; trimming leading zeros from a hash string; copy-paste truncation; user-supplied color codes, transaction IDs, or fingerprints with a missing digit.

Related errors


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

Appendix: source

Thrown at packages/zod/src/v4/core/util.ts:965

    binaryString += String.fromCharCode(bytes[i]);
  }
  return btoa(binaryString);
}

export function base64urlToUint8Array(base64url: string): InstanceType<typeof Uint8Array> {
  const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
  const padding = "=".repeat((4 - (base64.length % 4)) % 4);
  return base64ToUint8Array(base64 + padding);
}

export function uint8ArrayToBase64url(bytes: Uint8Array): string {
  return uint8ArrayToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}

export function hexToUint8Array(hex: string): InstanceType<typeof Uint8Array> {
  const cleanHex = hex.replace(/^0x/, "");
  if (cleanHex.length % 2 !== 0) {
    throw new Error("Invalid hex string length");
  }
  const bytes = new Uint8Array(cleanHex.length / 2);
  for (let i = 0; i < cleanHex.length; i += 2) {
    bytes[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
  }
  return bytes;
}

export function uint8ArrayToHex(bytes: Uint8Array): string {
  return Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

// instanceof
export abstract class Class {
  constructor(..._args: any[]) {}
}

View on GitHub (pinned to 2d90846af9)