Upload a file
  1. base64hub
  2. Learn Base64
  3. Base64 errors
Troubleshooting

Base64 Decoding Errors and How to Fix Them

Most Base64 failures come from five causes: the wrong alphabet, missing padding, stray characters, the wrong charset, or data that was encoded twice. Find your error message below and jump to the fix.

Error messages at a glance

MessageWhere you see itMost likely cause
binascii.Error: Incorrect paddingPython base64.b64decodeMissing = at the end, often from Base64URL or a cut-off string
binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4PythonTruncated input, or -/_ silently dropped
binascii.Error: Only base64 data is allowedPython with validate=TrueNewline, space, - or _ in the input
InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.Chrome, EdgeBase64URL characters, junk characters, bad length
InvalidCharacterError: String contains an invalid characterFirefoxSame as above
InvalidCharacterError: Invalid characterNode.js atobSame as above
IllegalArgumentException (“Illegal base64 character …”)Java java.util.Base64Wrong decoder for the alphabet, or line breaks
FormatException.NET Convert.FromBase64StringBase64URL characters or missing padding
base64: invalid inputGNU coreutils base64 -dCharacters outside the alphabet

Fixing decoder errors

“Incorrect padding” in Python

Base64 output always has a length that is a multiple of 4, padded with = at the end. Python’s decoder requires the padding:

>>> import base64
>>> base64.b64decode("SGVsbG8")
Traceback (most recent call last):
  ...
binascii.Error: Incorrect padding
>>> base64.b64decode("SGVsbG8=")
b'Hello'

Missing padding usually means the string is Base64URL (JWTs, URL tokens and many APIs drop =), or something trimmed it. Add the padding back:

s = "SGVsbG8"
s += "=" * (-len(s) % 4)

When the length is one more than a multiple of 4, padding cannot help. Python then reports number of data characters (5) cannot be 1 more than a multiple of 4. A single leftover character holds only 6 bits, so part of the data is missing. You can also hit this message with a string that is not truncated: unless you pass validate=True, Python discards characters outside the alphabet, including - and _. base64.b64decode("SGk_Pg==") drops the _ and ends up with 5 data characters. Use base64.urlsafe_b64decode for Base64URL input.

“Invalid character” and atob failures

Browsers implement atob with the same algorithm but word the error differently. Chrome says “The string to be decoded is not correctly encoded”, Firefox says “String contains an invalid character”, Node.js says “Invalid character”. All three throw an InvalidCharacterError for these inputs:

  • Base64URL characters. atob("SGk_Pg") fails because of _. Replace - with + and _ with / first.
  • A length of 4n+1 after whitespace is removed, such as atob("A").
  • Characters that do not belong: quotes copied from JSON, a data: prefix, a %2F from URL encoding.

atob does forgive some things. It skips spaces and newlines, and it accepts missing padding when the length works out, so atob("SGVsbG8") returns "Hello". The mirror error, btoa failing with “characters outside of the Latin1 range”, is about Unicode text and is covered in Uint8Array.toBase64.

A tolerant decoder that handles both alphabets, whitespace and missing padding:

function decodeLoose(s) {
  s = s.replace(/\s+/g, "").replace(/-/g, "+").replace(/_/g, "/");
  s += "=".repeat((4 - (s.length % 4)) % 4);
  return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
}

new TextDecoder().decode(decodeLoose("SGk_Pg")); // "Hi?>"

The Python equivalent:

import base64

def b64decode_any(s: str) -> bytes:
    s = "".join(s.split())
    s = s.replace("-", "+").replace("_", "/")
    s += "=" * (-len(s) % 4)
    return base64.b64decode(s, validate=True)

The Repair Base64 tool applies the same cleanup and tells you what it changed.

“Illegal base64 character” in Java and FormatException in .NET

Java’s java.util.Base64 ships three decoders, and each one is strict about its own alphabet. The message names the offending character in hex: 5f is _, 2d is -, 2f is /, 20 is a space, a is a newline.

Input containsUse
+ and /Base64.getDecoder()
- and _Base64.getUrlDecoder()
Line breaks (MIME, PEM bodies)Base64.getMimeDecoder()

The basic decoder rejects line breaks and spaces. It does accept missing padding, so SGVsbG8 decodes without complaint.

.NET’s Convert.FromBase64String throws FormatException for Base64URL characters and for missing padding. It skips spaces and line breaks. Fix the alphabet and padding as shown above before calling it. See Base64 in Java and Base64 in C# for complete examples.

The text comes out garbled

Decoding succeeded, but the text looks wrong. Base64 stores bytes, and the bytes were produced from text with some character encoding. Read them back with a different one and you get mojibake.

You seeOriginalWhat happened
CaféCaféUTF-8 bytes read as Latin-1 or Windows-1252
Caf�CaféWindows-1252 bytes read as UTF-8
H e l l o or H\0e\0l\0l\0o\0HelloUTF-16LE bytes read as UTF-8

The first row is the classic atob trap. atob("Q2Fmw6k=") returns "Café", because atob maps each byte to one character. Decode the bytes as UTF-8 instead:

const bytes = Uint8Array.from(atob("Q2Fmw6k="), (c) => c.charCodeAt(0));
new TextDecoder().decode(bytes); // "Café"

UTF-16LE is common with Windows tools: PowerShell’s -EncodedCommand and many .NET APIs produce it. Base64 to text lets you switch the charset until the output reads correctly, and Base64 to UTF-16 handles the wide-character case.

The output looks like binary

If the decoded result is full of �, control characters or PNG, PK and %PDF fragments, the payload is a file. Look at the first characters of the Base64: iVBORw0KGgo is a PNG, JVBERi0 a PDF, H4sI gzip. The full list is in common Base64 prefixes. Open the data with Base64 to file, which detects the format and offers a download. For H4sI use decode gzip Base64.

Decoding gives different bytes than expected

No error, but the bytes are wrong. These are the usual reasons:

  • A trailing newline at encoding time. echo "hello" | base64 gives aGVsbG8K (with \n), while echo -n "hello" | base64 gives aGVsbG8=. Use printf '%s' or echo -n.
  • + turned into a space. In URL query strings and form bodies, + means space. URLSearchParams("t=PGI+Ym9sZDwvYj4=").get("t") returns PGI Ym9sZDwvYj4=. Some decoders then throw, others drop the space and produce garbage. Percent-encode Base64 with encodeURIComponent before putting it in a URL, or use Base64URL.
  • Percent-encoding left in place. SGk%2FPg%3D%3D must be URL-decoded to SGk/Pg== before Base64 decoding.
  • Escapes copied from JSON. Copying a string out of raw JSON can bring in a literal \n (two characters). Python’s lenient decoder drops the backslash, keeps the n, and returns wrong bytes without an error: b64decode("SGVs\\nbG8=") gives b'Hel\x9d\xb1\xbc'. Parse the JSON instead of copying from it. The JSON Base64 decoder does this for every field.
  • Strict versus lenient whitespace. Python without validate=True, atob, .NET and the Java MIME decoder skip line breaks. Python with validate=True and the Java basic decoder reject them. MIME and PEM data is wrapped at 76 or 64 characters, so pick a decoder that accepts it.

Data that was encoded twice

Decoding U0dWc2JHOGdkMjl5YkdRaA== gives SGVsbG8gd29ybGQh, which is again valid Base64 and decodes to Hello world!. When the result of one decode consists only of Base64 characters, with a length that is a multiple of 4, suspect double encoding.

It often happens with Kubernetes Secrets. Values under data: are Base64, and kubectl create secret --from-literal encodes for you. If you encode the value yourself first, the pod receives Base64 text instead of the password. Use stringData: for plain values, or check what is stored with the Kubernetes Secret decoder. When in doubt, the Base64 validator shows whether a string is valid and how many bytes it holds.

FAQ

How do I get rid of binascii.Error: Incorrect padding?

Append = until the length is a multiple of 4: s += "=" * (-len(s) % 4). If the string is Base64URL, also replace - with + and _ with /, or use base64.urlsafe_b64decode.

Why does atob fail on a JWT?

JWTs use Base64URL, which has - and _. atob only knows + and /. Convert the characters first, or paste the token into the JWT decoder.

Why does my decoded text show é instead of é?

The bytes are UTF-8 but were read as Latin-1 or Windows-1252. Decode the bytes as UTF-8, for example with TextDecoder in JavaScript or .decode("utf-8") in Python.

Can a Base64 string with a length of 4n+1 be repaired?

No. One leftover character carries only 6 bits, less than a byte, so at least one character is missing. Find the original source or check where the string was cut.

How can I tell if data was Base64-encoded twice?

Decode it once. If the result is again a string of only A-Z a-z 0-9 + / = with a length that is a multiple of 4, decode it again.

Keep reading