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.

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)

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.