How to Fix 'Failed to execute btoa on Window: character outside Latin1 range'
Resolve the 'character outside Latin1 range' DOMException when calling btoa() on UTF-8 strings. Learn the modern TextEncoder solution.
The native browser window.btoa() function only supports characters within the Latin-1 range (code points 0x00 to 0xFF). When given Unicode strings containing emojis, accented characters, or non-Latin alphabets, it throws a DOMException: character outside Latin1 range. Fix it using the modern TextEncoder and Uint8Array API.
Solve this problem now with our free Base64 Encoder / Decoder
Try it instantly in your browser — zero tracking, 100% client-side privacy.
Specification & Architecture Reference
| Method | Unicode / Emoji Support | Recommended Modern Environment |
|---|---|---|
| window.btoa(str) | ❌ Throws DOMException for code points > 255 | Legacy ASCII-only strings |
| btoa(unescape(encodeURI(str))) | ⚠️ Deprecated, uses legacy unescape() | Legacy IE11 workarounds |
| TextEncoder + Uint8Array | ✅ Full UTF-8 compliant | Modern Browsers & Node.js 18+ |
| Buffer.from(str).toString('base64') | ✅ Full UTF-8 compliant | Node.js runtime server-side |
Why window.btoa Fails on Modern Strings
The name `btoa` stands for "binary to ASCII". Developed in early browser eras, the function assumes that every character in the provided string occupies exactly 1 byte (8 bits, representing the **Latin-1 / ISO-8859-1** character set).
However, modern JavaScript strings use **UTF-16 encoding**. When you pass characters with code points above 255—such as emojis (🚀), em-dashes (—), or non-Latin alphabets (e.g., Chinese, Arabic, Cyrillic)—`btoa` cannot fit the multi-byte code units into Latin-1 bytes and throws: ```text Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range. ```
The Modern, Standards-Compliant Solution Use the standardized **TextEncoder API** to encode the string into an array of UTF-8 bytes, then convert those bytes into a Base64 string:
function utf8ToBase64(str) {
const bytes = new TextEncoder().encode(str);
const binString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
return btoa(binString);function base64ToUtf8(base64) { const binString = atob(base64); const bytes = Uint8Array.from(binString, (m) => m.charCodeAt(0)); return new TextDecoder().decode(bytes); }
// Example: const encoded = utf8ToBase64("Hello 🌍! Mickytool makes dev tools."); console.log(encoded); // "SGVsbG8g8J+MjfCfkJkhIE1pY2t5dG9vbCBtYWtlcyBkZXYgdG9vbHMu" console.log(base64ToUtf8(encoded)); // Restored with emoji intact! ```
Avoid encoding errors by formatting and validating your Unicode strings in Mickytool's **Base64 Encoder / Decoder**, which handles multi-byte UTF-8 streams out of the box.
Solve this problem now with our free Base64 Encoder / Decoder
Execute this workflow immediately without saving data on remote servers.
Frequently Asked Questions
Why was encodeURI(str) used in older solutions?
Older workarounds used unescape(encodeURIComponent(str)) to turn multi-byte UTF-8 characters into percent-escaped byte values. However, unescape() is officially deprecated.
Does atob() also fail when decoding UTF-8 strings?
Yes. Calling atob() on Base64 produced from UTF-8 bytes produces a scrambled binary string (Mojibake) unless decoded via TextDecoder.