Home/Blog/DOMException: btoa() — The string contains characters outside of the Latin1 range
šŸ”¤
javascriptbase64encoding

DOMException: btoa() — The string contains characters outside of the Latin1 range

btoa() only handles Latin-1 characters. This error appears when you try to encode Unicode strings. Learn the correct two-step approach using encodeURIComponent + btoa(), and why you need it.

May 22, 20263 min readby ToolNinja

The Exact Error

DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Quick summary: btoa() can only encode characters in the Latin-1 range (codes 0-255). Any Unicode character above U+00FF — emojis, Chinese characters, Arabic text, the euro sign — triggers this error. The fix is to convert the string to UTF-8 bytes before encoding.


Why This Error Happens

btoa() (Binary To Ascii) treats each character as a single byte. Latin-1 maps one character to one byte, covering codes 0-255. Unicode characters above U+00FF don't fit in a single byte, so btoa() throws.

btoa('hello');      // 'aGVsbG8=' — OK (all ASCII)
btoa('cafe');       // OK (all Latin-1)
btoa('€100');       // THROWS — '€' is U+20AC, above U+00FF
btoa('Hello šŸ‘‹');   // THROWS — emoji is above U+00FF
btoa('你儽');        // THROWS — CJK characters above U+00FF

Step-by-Step Diagnosis

Step 1 — Find the non-Latin-1 character

function findNonLatin1(str) {
  for (let i = 0; i < str.length; i++) {
    const code = str.charCodeAt(i);
    if (code > 255) {
      console.log(`Non-Latin1 at position ${i}: '${str[i]}' (U+${code.toString(16).toUpperCase()})`);
    }
  }
}
findNonLatin1('Hello €100');
// Non-Latin1 at position 6: '€' (U+20AC)

Step 2 — Determine what you're encoding

  • Text content — Use encodeURIComponent + btoa
  • Binary data — Use Uint8Array — btoa
  • Node.js — Use Buffer

Solutions

Solution 1 — Use encodeURIComponent before btoa

function base64Encode(str) {
  return btoa(encodeURIComponent(str));
}

function base64Decode(str) {
  return decodeURIComponent(atob(str));
}

const encoded = base64Encode('Hello šŸ‘‹ 你儽 €100');
const decoded = base64Decode(encoded);  // 'Hello šŸ‘‹ 你儽 €100'

Solution 2 — Use TextEncoder + Uint8Array

function base64EncodeUnicode(str) {
  const bytes = new TextEncoder().encode(str);
  const binString = Array.from(bytes, b => String.fromCodePoint(b)).join('');
  return btoa(binString);
}

function base64DecodeUnicode(base64) {
  const binString = atob(base64);
  const bytes = Uint8Array.from(binString, c => c.codePointAt(0));
  return new TextDecoder().decode(bytes);
}

Solution 3 — Node.js Buffer approach

const encoded = Buffer.from('Hello šŸ‘‹ 你儽', 'utf8').toString('base64');
const decoded = Buffer.from(encoded, 'base64').toString('utf8');

Solution 4 — URL-safe Base64

function base64UrlEncode(str) {
  return btoa(encodeURIComponent(str))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

Quick Reference

ScenarioWorks with btoa()?Fix
ASCII text onlyYesUse directly
Latin-1 text (Ć©, Ʊ)Yes (codes ≤255)Use directly
Euro sign €, emojisNo (> U+00FF)btoa(encodeURIComponent(str))
Node.js serverN/ABuffer.from(str, 'utf8').toString('base64')

Prevent This Error in the Future

1. Never use bare btoa(text) for user-provided strings. Always use the encodeURIComponent wrapper.

2. Create helper functions once and reuse everywhere:

const b64encode = str => btoa(encodeURIComponent(str));
const b64decode = str => decodeURIComponent(atob(str));

3. In Node.js, use Buffer always — it handles UTF-8 natively.


Use ToolNinja to Debug Faster

The Base64 tool handles Unicode encoding and decoding correctly — paste any string including emojis, CJK characters, and accented text to get the correct Base64 output.

šŸ”§ Base64 Encoder/Decoder — toolninja.io/tools/base64

Share:š• Twitterin LinkedIn

Frequently Asked Questions

Why does btoa() only support Latin-1 characters?

btoa() was designed to encode binary data (Latin-1 = one byte per character). Base64 encoding works on raw bytes, and Latin-1 maps exactly one character to one byte (character codes 0-255). Unicode characters above U+00FF require multiple bytes in UTF-8 and btoa() doesn't handle multi-byte characters.

What is the difference between btoa() and Base64 encoding a UTF-8 string?

btoa() converts a binary string where each character is a byte value 0-255. To encode a UTF-8 Unicode string, you first convert it to UTF-8 bytes with encodeURIComponent, then Base64-encode those bytes with btoa. The result is longer because multi-byte characters require multiple bytes.

Is atob(btoa(str)) === str always true?

Only for Latin-1 strings. For Unicode strings encoded with encodeURIComponent, you need the reverse: decodeURIComponent(atob(encoded)). The encoding and decoding approach must match exactly.

Should I use btoa/atob or a library for Base64 in modern JavaScript?

For simple Base64 encoding of UTF-8 text in browsers, the encodeURIComponent+btoa pattern is reliable. In Node.js 16+, use Buffer.from(str, 'utf8').toString('base64') — it handles Unicode correctly without workarounds.

🄷 ToolNinja