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

  1. Strip non-hex characters and prefixes before the call: str = str.replace(/^0x/, '').replace(/[^0-9a-fA-F]/g, '')
  2. Validate the string first with /^(?:[0-9a-fA-F]{2})+$/ before calling Uint8Array.fromHex
  3. If the data is not actually hex, use Uint8Array.fromBase64 (core-js also polyfills it) or atob instead
  4. 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

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


AI-assisted analysis of zloirock/core-js@84e45fba09 (2026-08-30). Data as JSON: /api/errors/5bbc72e953fed5d2. Report an issue: GitHub.