zloirock/core-js · error · SyntaxError
String should only contain hex characters
Error message
String should only contain hex characters
What it means
This SyntaxError comes from the core-js polyfill for Uint8Array.fromHex. It means the input string contained a pair of characters that, when interpreted as a hexadecimal byte literal ('0x' + pair + '0'), did not parse to a number (NaN), i.e. the string contains non-hex characters. The polyfill deliberately rejects such input because the spec requires hex-only strings.
Source
Thrown at packages/core-js/internals/uint8-from-hex.js:21
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
- Strip non-hex characters and prefixes before the call: str = str.replace(/^0x/, '').replace(/[^0-9a-fA-F]/g, '')
- Validate the string first with /^(?:[0-9a-fA-F]{2})+$/ before calling Uint8Array.fromHex
- If the data is not actually hex, use Uint8Array.fromBase64 (core-js also polyfills it) or atob instead
- Ensure the cleaned string has even length; pad with a leading '0' if a nibble was dropped
Example fix
// before
const bytes = Uint8Array.fromHex('0xdeadbeef');
// after
const hex = '0xdeadbeef'.replace(/^0x/, '');
if (!/^(?:[0-9a-fA-F]{2})+$/.test(hex)) throw new Error('invalid hex');
const bytes = Uint8Array.fromHex(hex); Defensive patterns
Strategy: validation
Validate before calling
function isValidHex(s) {
return typeof s === 'string' && /^(?:[0-9a-fA-F]{2})+$/.test(s);
}
if (!isValidHex(input)) throw new Error('input must be an even-length hex string');
const bytes = Uint8Array.fromHex(input); Type guard
function isHexString(v) {
return typeof v === 'string' && v.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(v);
} Try / catch
let bytes;
try {
bytes = Uint8Array.fromHex(input);
} catch (e) {
if (e instanceof SyntaxError && /hex characters/.test(e.message)) {
bytes = Uint8Array.fromHex(input.replace(/[^0-9a-fA-F]/g, ''));
} else throw e;
} Prevention
- Always validate user-supplied hex with a regex before parsing
- Strip '0x' prefixes and separators (spaces, colons, dashes) at the boundary where the string enters your system
- Use Uint8Array.fromBase64 for base64 data, not fromHex
- Keep hex strings lowercase and even-length by convention
When it happens
Trigger: Calling Uint8Array.fromHex() (or the underlying fromHex function in uint8-from-hex.js) with a string containing any characters outside [0-9a-fA-F], or a string whose length is not a multiple of two so the /([\S\s]{2})/g segmentation pairs a hex digit with a non-hex trailing character.
Common situations: Passing base64 or base64url data to fromHex instead of fromBase64; hex strings copied from logs that include '0x' prefixes, spaces, colons ('aa:bb:cc'), or whitespace between bytes; user-supplied input that was never validated; uppercase '0X' prefix left on the string.
Related errors
- Padding is too early
- Malformed padding: only one =
- String should be an even number of characters
- Failed to parse number at: ${i}
- Failed to parse number's fraction at: ${i}
AI-assisted analysis of zloirock/core-js@84e45fba09 (2026-08-30).
Data as JSON: /api/errors/5bbc72e953fed5d2.
Report an issue: GitHub.