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
- Pad the hex string to an even length with a leading zero: `hex.length % 2 ? '0' + hex : hex`.
- Strip whitespace and any non-hex characters before conversion, then re-check length.
- 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
- Pad hex columns/fields to fixed even widths at the storage boundary.
- Validate hex input (regex + even length) before passing to codec helpers.
- Avoid trimming leading zeros from hash strings; store them with full width.
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
- Unrecognized hash format
- Cannot create literal schema with no valid values
- Cannot overwrite keys on object schemas containing…
- Cannot specify both `message` and `error` params
- invalid_arguments
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)