zloirock/core-js · error · SyntaxError
Extra bits
Error message
Extra bits
What it means
In Uint8Array.fromBase64 (as polyfilled by core-js), a final base64 chunk of 2 characters encodes 2 bytes but still carries 6 leftover bits of the third byte. With lastChunkHandling 'strict', if those leftover bits are non-zero the input isn't canonical base64, so a SyntaxError 'Extra bits' is thrown. 'loose' mode ignores this.
Source
Thrown at packages/core-js/internals/uint8-from-base64.js:48
if (chunkLength < 4) {
chunk += chunkLength === 2 ? 'AA' : 'A';
}
var triplet = (alphabet[at(chunk, 0)] << 18)
+ (alphabet[at(chunk, 1)] << 12)
+ (alphabet[at(chunk, 2)] << 6)
+ alphabet[at(chunk, 3)];
var chunkBytes = [
(triplet >> 16) & 255,
(triplet >> 8) & 255,
triplet & 255
];
if (chunkLength === 2) {
if (throwOnExtraBits && chunkBytes[1] !== 0) {
throw new SyntaxError('Extra bits');
}
return [chunkBytes[0]];
}
if (chunkLength === 3) {
if (throwOnExtraBits && chunkBytes[2] !== 0) {
throw new SyntaxError('Extra bits');
}
return [chunkBytes[0], chunkBytes[1]];
}
return chunkBytes;
};
var writeBytes = function (bytes, elements, written) {
var elementsLength = elements.length;
for (var index = 0; index < elementsLength; index++) {
bytes[written + index] = elements[index];View on GitHub (pinned to 84e45fba09)
Solutions
- Use lastChunkHandling: 'loose' (the default) if you accept non-canonical input.
- Re-encode the data at the source with a canonical base64 encoder.
- Strip/normalize the final partial chunk before decoding.
- Drop 'strict' only if bitwise-exact round-tripping isn't required for security (strict exists to catch tampering).
Example fix
// before
Uint8Array.fromBase64('QR==', { lastChunkHandling: 'strict' }); // SyntaxError: Extra bits
// after
Uint8Array.fromBase64('QQ==', { lastChunkHandling: 'strict' }); // canonical, ok Defensive patterns
Strategy: validation
Validate before calling
function isCanonicalTail2(b64, alphabet) {
const m = b64.match(/(..?)$/);
return m ? true : false; // prefer re-encoding: canonical iff low bits zero
}
// simplest pre-check: re-encode decoded bytes and compare when using strict
const bytes = Uint8Array.fromBase64(b64); // loose
if (btoa(String.fromCharCode(...bytes)) !== b64) throw new Error('non-canonical base64'); Type guard
function isCanonicalBase64(s) {
try { return btoa(String.fromCharCode(...Uint8Array.fromBase64(s))) === s; }
catch { return false; }
} Try / catch
try {
bytes = Uint8Array.fromBase64(b64, { lastChunkHandling: 'strict' });
} catch (e) {
if (e instanceof SyntaxError && e.message === 'Extra bits') {
bytes = Uint8Array.fromBase64(b64); // loose fallback
} else throw e;
} Prevention
- Encode with a canonical base64 implementation (zero trailing bits).
- Only use 'strict' when canonicality matters (e.g. signature verification).
- Round-trip test your encoder: decode then re-encode and compare strings.
When it happens
Trigger: `Uint8Array.fromBase64(str, { lastChunkHandling: 'strict' })` where the string ends in a 2-char final chunk (no '==') whose low bits are non-zero, e.g. 'QQ==' is fine but 'QR==' style non-canonical endings throw.
Common situations: Data encoded by a non-canonical or hand-rolled encoder that didn't zero the trailing bits, strings truncated by a regex/split that dropped padding, or switching code from loose/decoder defaults to strict mode.
Related errors
- Malformed padding: exactly one additional character
- Missing padding
- Incorrect `lastChunkHandling` option
- Cannot convert a Symbol value to a string
- Wrong length
AI-assisted analysis of zloirock/core-js@84e45fba09 (2026-08-30).
Data as JSON: /api/errors/90ba4526a8e72141.
Report an issue: GitHub.