zloirock/core-js · error · SyntaxError
String should be an even number of characters
Error message
String should be an even number of characters
What it means
Uint8Array.fromHex() requires the input to be a hex string with an even number of characters, since every byte is exactly two hex digits. If string.length % 2 !== 0 (a nibble is missing), decoding is ambiguous and the library throws this SyntaxError before any bytes are written.
Source
Thrown at packages/core-js/internals/uint8-from-hex.js:12
'use strict';
var globalThis = require('../internals/global-this');
var uncurryThis = require('../internals/function-uncurry-this');
var Uint8Array = globalThis.Uint8Array;
var SyntaxError = globalThis.SyntaxError;
var min = Math.min;
var stringMatch = uncurryThis(''.match);
module.exports = function (string, into) {
var stringLength = string.length;
if (stringLength % 2 !== 0) throw new SyntaxError('String should be an even number of characters');
var maxLength = into ? min(into.length, stringLength / 2) : stringLength / 2;
var bytes = into || new Uint8Array(maxLength);
var segments = stringMatch(string, /[\S\s]{2}/g);
var written = 0;
for (; written < maxLength; written++) {
var result = +('0x' + segments[written] + '0');
// eslint-disable-next-line no-self-compare -- NaN check
if (result !== result) {
throw new SyntaxError('String should only contain hex characters');
}
bytes[written] = result >> 4;
}
return { bytes: bytes, read: written << 1 };
};
View on GitHub (pinned to 84e45fba09)
Solutions
- Pad the string to even length with a leading '0' if a single leading nibble was dropped: s.length % 2 ? '0' + s : s.
- Fix the producer to zero-pad each byte to two hex digits (e.g. n.toString(16).padStart(2, '0')).
- Verify the string wasn't truncated in storage/transport; compare length to the expected byteCount * 2.
- Catch SyntaxError at the boundary and report 'odd-length hex string' to the caller.
Example fix
// before
Uint8Array.fromHex('4a617'); // SyntaxError: String should be an even number of characters
// after
const hex = raw.length % 2 === 0 ? raw : '0' + raw;
const bytes = Uint8Array.fromHex(hex); Defensive patterns
Strategy: validation
Validate before calling
function isEvenLengthHex(s) {
return typeof s === 'string' && /^[0-9a-fA-F]*$/.test(s) && s.length % 2 === 0;
}
// if (!isEvenLengthHex(input)) input = input.padStart(input.length + (input.length % 2), '0'); Type guard
function isHexString(s) {
return typeof s === 'string' && s.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(s);
} Try / catch
try {
const bytes = Uint8Array.fromHex(input);
} catch (e) {
if (e instanceof SyntaxError && e.message.includes('even number of characters')) {
throw new TypeError(`Hex string must have even length, got ${input.length}`);
}
throw e;
} Prevention
- Zero-pad hex bytes at the source: n.toString(16).padStart(2, '0').
- Avoid '0x' prefixes leaking into concatenated hex strings.
- Validate length === expectedByteCount * 2 before decoding.
- Guard copy/paste and truncation by checking even length at the input boundary.
When it happens
Trigger: Calling Uint8Array.fromHex('abc') or fromHexInto with any odd-length string — e.g. a hex string missing its last character, produced by a '%x' format that dropped a leading zero ('0x5' instead of '05' concatenated), or manually built nibble-by-nibble.
Common situations: Formatting individual bytes with '0x%x' without zero-padding so '0x0A' becomes 'a', string truncation by fixed-length buffers, copy/paste dropping a character, or concatenating hex nibbles instead of byte pairs.
Related errors
- Padding is too early
- Malformed padding: only one =
- Unexpected character
- String should only contain hex characters
- Unexpected character after padding
AI-assisted analysis of zloirock/core-js@84e45fba09 (2026-08-30).
Data as JSON: /api/errors/e75a6b1f09d98d4d.
Report an issue: GitHub.