gchq/CyberChef · error · OperationError

Error: Base64 input contains non-alphabet char(s)

Error message

Error: Base64 input contains non-alphabet char(s)

What it means

Thrown by fromBase64() in src/core/lib/Base64.mjs:140 during the decode loop, under strictMode, when alphabet.indexOf() returns -1 for any of the four quartet characters — meaning a byte survived into the loop that is not in the alphabet (including the pad slot). Because the earlier removeNonAlphChars pass strips everything outside the alphabet+pad, this error in practice requires removeNonAlphChars=false so that foreign characters reach the decoder.

Source

Thrown at src/core/lib/Base64.mjs:140

                }
            }
        }
    }

    const output = [];
    let chr1, chr2, chr3,
        enc1, enc2, enc3, enc4,
        i = 0;

    while (i < data.length) {
        // Including `|| null` forces empty strings to null so that indexOf returns -1 instead of 0
        enc1 = alphabet.indexOf(data.charAt(i++) || null);
        enc2 = alphabet.indexOf(data.charAt(i++) || null);
        enc3 = alphabet.indexOf(data.charAt(i++) || null);
        enc4 = alphabet.indexOf(data.charAt(i++) || null);

        if (strictMode && (enc1 < 0 || enc2 < 0 || enc3 < 0 || enc4 < 0)) {
            throw new OperationError("Error: Base64 input contains non-alphabet char(s)");
        }

        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;

        if (chr1 >= 0 && chr1 < 256) {
            output.push(chr1);
        }
        if (chr2 >= 0 && chr2 < 256 && enc3 !== 64) {
            output.push(chr2);
        }
        if (chr3 >= 0 && chr3 < 256 && enc4 !== 64) {
            output.push(chr3);
        }
    }

    return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Pass removeNonAlphChars=true (the default) so out-of-alphabet characters are stripped before decoding.
  2. Pre-trim the input: data.replace(/[^A-Za-z0-9+/=]/g, '') for the standard alphabet.
  3. Verify the alphabet option matches how the string was produced (Standard vs URL-safe vs itoa64).
  4. If you genuinely need strict rejection, keep strictMode=true but clean the input first.

Example fix

// before - space not in alphabet, removal disabled
fromBase64('AB CD', 'A-Za-z0-9+/=', 'byteArray', false, true);

// after - let the library strip non-alphabet chars
fromBase64('AB CD', 'A-Za-z0-9+/=', 'byteArray', true, true);
Defensive patterns

Strategy: validation

Validate before calling

function containsOnlyAlphabet(data, alphabet) {
  for (const ch of data) {
    if (alphabet.indexOf(ch) < 0) return false;
  }
  return true;
}
// or simply pass removeNonAlphChars=true to fromBase64

Type guard

function isPureStandardBase64(s) {
  return typeof s === 'string' && /^[A-Za-z0-9+/=]*$/.test(s);
}

Try / catch

try {
  fromBase64(input, 'A-Za-z0-9+/=', 'byteArray', false, true);
} catch (e) {
  if (e instanceof OperationError && /non-alphabet char/.test(e.message)) {
    input = input.replace(/[^A-Za-z0-9+/=]/g, '');
  }
}

Prevention

When it happens

Trigger: fromBase64(data, alphabet, returnType, removeNonAlphChars=false, strictMode=true) where data contains any char not in the expanded alphabet. Example: fromBase64('AB CD', 'A-Za-z0-9+/=', 'byteArray', false, true) — the space is not in the alphabet, indexOf returns -1, throws.

Common situations: Whitespace or newlines in pasted Base64 when the caller explicitly disabled character removal; mixing alphabets (e.g. url-safe '-/_' fed to the standard alphabet); embedded null bytes or BOM; binary data mistakenly treated as a Base64 string.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/55df5a17e2816dec. Report an issue: GitHub.