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
- Verify the string is complete, correctly padded base64 (length % 4 === 0 after padding) and uses the standard alphabet (+ and /, not - and _).
- If the source is base64url, convert it first: replace '-' with '+' and '_' with '/', then pad with '=' to a multiple of 4.
- 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.
- 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
- Normalize base64url to standard base64 (replace -/_, re-pad) before atob.
- Check length % 4 !== 1 and alphabet membership before decoding.
- Never truncate base64 values when logging, storing, or splitting tokens.
- Trim whitespace/newlines from copied values before decoding.
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
- InvalidCharacterError
- Unexpected character
- ArrayBuffer expected
- Target is not a typed array
- Object already initialized
AI-assisted analysis of zloirock/core-js@84e45fba09 (2026-08-30).
Data as JSON: /api/errors/3c1af98e0a92be0b.
Report an issue: GitHub.