zloirock/core-js · error · DOMException
InvalidCharacterError
InvalidCharacterError
Error message
The string contains characters outside of the Latin1 range
What it means
core-js's btoa polyfill (packages/core-js/modules/web.btoa.js:52) throws a DOMException 'InvalidCharacterError' when the input string contains a character whose char code exceeds 0xFF, because btoa can only encode Latin1 (byte) strings. This matches native window.btoa semantics: it encodes each character as one byte, so anything above U+00FF is rejected.
Source
Thrown at packages/core-js/modules/web.btoa.js:52
$({ global: true, bind: true, enumerable: true, forced: !BASIC || NO_ARG_RECEIVING_CHECK || WRONG_ARG_CONVERSION || WRONG_ARITY }, {
btoa: function btoa(data) {
validateArgumentsLength(arguments.length, 1);
// `webpack` dev server bug on IE global methods - use call(fn, global, ...)
if (BASIC) return call($btoa, globalThis, toString(data));
var string = toString(data);
// (string.length + 2) / 3) and then truncating to integer
// does the ceil automatically. << 2 will truncate the integer
// while also doing *4. ceil(length / 3) quanta, 4 bytes output
// per quanta for base64.
var output = new $Array((string.length + 2) / 3 << 2);
var outputIndex = 0;
var position = 0;
var map = i2c;
var block, charCode;
while (charAt(string, position) || (map = '=', position % 1)) {
charCode = charCodeAt(string, position += 3 / 4);
if (charCode > 0xFF) {
throw new (getBuiltIn('DOMException'))('The string contains characters outside of the Latin1 range', 'InvalidCharacterError');
}
block = block << 8 | charCode;
output[outputIndex++] = charAt(map, 63 & block >> 8 - position % 1 * 8);
} return join(output, '');
}
});
View on GitHub (pinned to 84e45fba09)
Solutions
- Encode to UTF-8 bytes before btoa: `btoa(String.fromCharCode(...new TextEncoder().encode(str)))` (chunk large strings to avoid argument limits).
- Alternatively use the classic trick: `btoa(unescape(encodeURIComponent(str)))`.
- If the environment allows, use modern `Buffer.from(str, 'utf-8').toString('base64')` (Node) or fetch-base64 helpers instead of btoa.
- Pre-validate with a check that every char code is <= 0xFF if you intentionally require Latin1-only input.
Example fix
// before
const encoded = btoa(JSON.stringify(payload));
// after
const bytes = new TextEncoder().encode(JSON.stringify(payload));
const latin1 = Array.from(bytes, b => String.fromCharCode(b)).join('');
const encoded = btoa(latin1); Defensive patterns
Strategy: validation
Validate before calling
function isLatin1(s) {
if (typeof s !== 'string') return false;
for (let i = 0; i < s.length; i++) {
if (s.charCodeAt(i) > 0xFF) return false;
}
return true;
}
if (!isLatin1(input)) {
input = String.fromCharCode(...new TextEncoder().encode(input));
}
const encoded = btoa(input); Type guard
function isLatin1String(v) {
return typeof v === 'string' && !/[^\u0000-\u00ff]/.test(v);
} Try / catch
let encoded;
try {
encoded = btoa(input);
} catch (e) {
if (e.name === 'InvalidCharacterError') {
encoded = btoa(String.fromCharCode(...new TextEncoder().encode(input)));
} else {
throw e;
}
} Prevention
- Always route non-ASCII text through TextEncoder (or encodeURIComponent trick) before btoa.
- Treat btoa as a byte-string encoder, not a text encoder; document this in team guidelines.
- Test base64 encoding with emoji and multilingual fixtures, not just ASCII.
- On Node, prefer Buffer.from(str, 'utf-8').toString('base64') which handles Unicode directly.
When it happens
Trigger: Calling `btoa(str)` where str contains any non-Latin1 character: emoji, CJK text, accented characters beyond U+00FF (e.g. 'é' is fine at U+00E9, but 'ā' U+0101, '€' U+20AC, or any emoji are not), typically from user input or decoded JSON.
Common situations: Base64-encoding user-generated content that includes Unicode; encoding JSON payloads with internationalized text; strings read from UTF-8 sources and passed directly to btoa; code that worked with ASCII test data but fails with real-world multilingual input.
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/128d95e948983d4a.
Report an issue: GitHub.