zloirock/core-js · error · DOMException

InvalidCharacterError

InvalidCharacterError

Error message

The string is not correctly encoded

What it means

core-js's atob polyfill (packages/core-js/modules/web.atob.js:61) throws a DOMException 'InvalidCharacterError' when the input string is not valid base64: its length mod 4 is 1 after stripping trailing '=' padding, or it contains characters outside the base64 alphabet (whitespace is tolerated per spec). This mirrors the native window.atob behavior defined by the HTML Standard.

Solutions

  1. Verify the string is complete, correctly padded base64 (length % 4 === 0 after padding) and uses the standard alphabet (+ and /, not - and _).
  2. If the source is base64url, convert it first: replace '-' with '+' and '_' with '/', then pad with '=' to a multiple of 4.
  3. If the value is actually base64-encoded UTF-8 text, decode bytes with atob then decode with TextDecoder('utf-8') rather than expecting atob to return text.
  4. Trim surrounding whitespace/newlines before calling atob (interior invalid characters still fail).

Example fix

// before
const json = JSON.parse(atob(token.split('.')[1]));
// after
let b64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
while (b64.length % 4) b64 += '=';
const json = JSON.parse(atob(b64));
Defensive patterns

Strategy: validation

Validate before calling

function isCanonicalBase64(s) {
  return typeof s === 'string'
    && /^[A-Za-z0-9+/]*={0,2}$/.test(s.trim())
    && ((s.trim().length + 3) & ~3) % 4 === s.trim().length % 4 || /^[A-Za-z0-9+/]*={0,2}$/.test(s.trim());
}
// stricter: validate length mod 4 after stripping padding
function isValidBase64(s) {
  if (typeof s !== 'string') return false;
  const t = s.trim().replace(/=+$/, '');
  return /^[A-Za-z0-9+/]*$/.test(t) && t.length % 4 !== 1;
}

Type guard

function isBase64String(v) {
  return typeof v === 'string'
    && /^[A-Za-z0-9+/]*={0,2}$/.test(v)
    && (v.length % 4 === 0);
}

Try / catch

let decoded;
try {
  decoded = atob(input);
} catch (e) {
  if (e.name === 'InvalidCharacterError') {
    throw new Error(`Malformed base64 input: ${input.slice(0, 20)}...`, { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `atob(str)` where str has a leftover length of 1 modulo 4 (bad padding), or contains any character not in A-Z a-z 0-9 + / = (e.g. '-', '_', spaces inside the string, URL-safe base64, or a truncated/corrupted base64 value).

Common situations: Decoding JWT segments or tokens that were cut off in transit/storage; decoding base64url (base64 with '-' and '_') produced by other systems; strings that were HTML-escaped or contain newlines copied from logs; base64 produced without padding from a non-standard encoder.

Related errors


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

Appendix: source

Thrown at packages/core-js/modules/web.atob.js:61

// `atob` method
// https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob
$({ global: true, bind: true, enumerable: true, forced: FORCED }, {
  atob: function atob(data) {
    validateArgumentsLength(arguments.length, 1);
    // `webpack` dev server bug on IE global methods - use call(fn, global, ...)
    if (BASIC && !NO_SPACES_IGNORE && !NO_ENCODING_CHECK) return call($atob, globalThis, data);
    var string = replace(toString(data), whitespaces, '');
    var position = 0;
    var bc = 0;
    var length, chr, bs;
    if (!(string.length & 3)) {
      string = replace(string, finalEq, '');
    }
    length = string.length;
    var lenmod = length & 3;
    if (lenmod === 1 || exec(disallowed, string)) {
      throw new (getBuiltIn('DOMException'))('The string is not correctly encoded', 'InvalidCharacterError');
    }
    // (length >> 2) is equivalent for length / 4 floored; * 3 then multiplies the
    // number of bytes for full quanta
    // lenmod is length % 4; if there's 2 or 3 bytes it's 1 or 2 bytes of extra output
    // respectively, so -1, however use a ternary to ensure 0 does not get -1 onto length
    var output = new $Array((length >> 2) * 3 + (lenmod ? lenmod - 1 : 0));
    var outputIndex = 0;
    while (position < length) {
      chr = charAt(string, position++);
      bs = bc & 3 ? (bs << 6) + c2i[chr] : c2i[chr];
      if (bc++ & 3) output[outputIndex++] = fromCharCode(255 & bs >> (-2 * bc & 6));
    }
    return join(output, '');
  }
});

View on GitHub (pinned to 84e45fba09)