{"id":"d3a2888ede6d9a5b","repo":"colinhacks/zod","slug":"invalid-hex-string-length","errorCode":null,"errorMessage":"Invalid hex string length","messagePattern":"Invalid hex string length","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v4/core/util.ts","lineNumber":965,"sourceCode":"    binaryString += String.fromCharCode(bytes[i]);\n  }\n  return btoa(binaryString);\n}\n\nexport function base64urlToUint8Array(base64url: string): InstanceType<typeof Uint8Array> {\n  const base64 = base64url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n  const padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n  return base64ToUint8Array(base64 + padding);\n}\n\nexport function uint8ArrayToBase64url(bytes: Uint8Array): string {\n  return uint8ArrayToBase64(bytes).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n\nexport function hexToUint8Array(hex: string): InstanceType<typeof Uint8Array> {\n  const cleanHex = hex.replace(/^0x/, \"\");\n  if (cleanHex.length % 2 !== 0) {\n    throw new Error(\"Invalid hex string length\");\n  }\n  const bytes = new Uint8Array(cleanHex.length / 2);\n  for (let i = 0; i < cleanHex.length; i += 2) {\n    bytes[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);\n  }\n  return bytes;\n}\n\nexport function uint8ArrayToHex(bytes: Uint8Array): string {\n  return Array.from(bytes)\n    .map((b) => b.toString(16).padStart(2, \"0\"))\n    .join(\"\");\n}\n\n// instanceof\nexport abstract class Class {\n  constructor(..._args: any[]) {}\n}","sourceCodeStart":947,"sourceCodeEnd":983,"githubUrl":"https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/util.ts#L947-L983","documentation":"Thrown by `util.hexToUint8Array` when the input string has an odd length after stripping a leading `0x` prefix (guard at util.ts:964). Hex bytes are pairs of characters, so an odd-length string cannot be parsed into bytes — the function rejects rather than guess padding. It's used internally by hash decoders and any consumer calling the util directly.","triggerScenarios":"Calling `hexToUint8Array('abc')` (3 chars), `'0x1f3'` (3 after prefix), or any hex string with an odd character count. Also reached indirectly via `z.string().format('hash')` or custom code paths that decode hex hashes.","commonSituations":"Truncated or partially-trimmed hash strings from logs; copy-paste losing a character; concatenating hex fragments that joined to odd length; user input not pre-validated before being passed to a hash decoder.","solutions":["Pre-validate length: `if (hex.replace(/^0x/, '').length % 2 !== 0) throw new Error('hex must be even-length')`.","Pad with a leading zero if semantically correct: `hex.padStart(targetLen, '0')`.","Regenerate the source string from `util.uint8ArrayToHex(bytes)` to guarantee even length."],"exampleFix":"// before\nconst bytes = util.hexToUint8Array(rawHash); // throws if odd-length\n\n// after\nconst clean = rawHash.replace(/^0x/, '');\nconst bytes = util.hexToUint8Array(clean.length % 2 ? '0' + clean : clean);","handlingStrategy":"validation","validationCode":"function toBytes(hex: string) {\n  const clean = hex.replace(/^0x/, '');\n  if (clean.length % 2 !== 0) throw new Error('hex string must have even length');\n  return util.hexToUint8Array(clean);\n}","typeGuard":"function isEvenHex(s: string): boolean {\n  return /^[0-9a-fA-F]*$/.test(s.replace(/^0x/, '')) &&\n    s.replace(/^0x/, '').length % 2 === 0;\n}","tryCatchPattern":"try { bytes = util.hexToUint8Array(raw); }\ncatch (e) {\n  if (e instanceof Error && /hex string length/i.test(e.message)) {\n    bytes = util.hexToUint8Array(raw.replace(/^0x/, '').padStart(/* even */, '0'));\n  } else throw e;\n}","preventionTips":["Normalize hex strings before decoding: strip `0x`, lowercase, pad to even length.","Validate input shape at API boundaries before passing to hash decoders."],"tags":["zod","hex","encoding","validation"],"analyzedSha":"912f0f51b0ced654d0069741e7160834dca742ee","analyzedAt":"2026-08-03T17:41:55.908Z","schemaVersion":2}