maotoumao/MusicFree · error · Error

'btoa' failed: The string to be encoded contains characters

Error message

'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.

What it means

The pure-JS btoa polyfill in src/utils/base64.ts throws this when any character code in the input string exceeds 0xFF (non-Latin1, e.g. Chinese text, emoji). Browser btoa has the same restriction; the polyfill replicates it. Input must be a binary/Latin1 string, not arbitrary UTF-8 text.

Source

Thrown at src/utils/base64.ts:21

// @flow
// Inspired by: https://github.com/davidchambers/Base64.js/blob/master/base64.js

const chars =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
const Base64 = {
    btoa: (input: string = "") => {
        let str = input;
        let output = "";

        for (
            let block = 0, charCode, i = 0, map = chars;
            str.charAt(i | 0) || ((map = "="), i % 1);
            output += map.charAt(63 & (block >> (8 - (i % 1) * 8)))
        ) {
            charCode = str.charCodeAt((i += 3 / 4));

            if (charCode > 0xff) {
                throw new Error(
                    "'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.",
                );
            }

            block = (block << 8) | charCode;
        }

        return output;
    },

    atob: (input: string = "") => {
        let str = input.replace(/[=]+$/, "");
        let output = "";

        if (str.length % 4 == 1) {
            throw new Error(
                "'atob' failed: The string to be decoded is not correctly encoded.",
            );

View on GitHub (pinned to d118b18b3d)

Solutions

  1. Convert the string to UTF-8 bytes first: btoa(unescape(encodeURIComponent(str))) or use TextEncoder + manual base64.
  2. Better: add a utf8ToB64 helper in base64.ts that encodes via encodeURIComponent/unescape before the loop.
  3. If the payload is data, encode bytes (Uint8Array) instead of a JS string.

Example fix

// before
const encoded = btoa(JSON.stringify(payload));
// after
const encoded = btoa(unescape(encodeURIComponent(JSON.stringify(payload))));
Defensive patterns

Strategy: validation

Validate before calling

function isLatin1(str) {
  for (let i = 0; i < str.length; i++) if (str.charCodeAt(i) > 0xff) return false;
  return true;
}
if (!isLatin1(payload)) payload = unescape(encodeURIComponent(payload));

Try / catch

try {
  return btoa(str);
} catch (e) {
  if (String(e).includes('Latin1')) return btoa(unescape(encodeURIComponent(str)));
  throw e;
}

Prevention

When it happens

Trigger: Calling btoa(str) where str contains characters with code point > 255 — e.g. base64-encoding a JSON payload containing Chinese lyrics/titles, emoji, or any non-ASCII text.

Common situations: Encoding user-entered Chinese song names or config strings; encoding a UTF-8 string that was never percent-encoded or byte-converted first; porting browser code that assumed ASCII-only input.

Related errors


AI-assisted analysis of maotoumao/MusicFree@d118b18b3d (2026-08-30). Data as JSON: /api/errors/4f9d5e7f66c092fe. Report an issue: GitHub.