Common causes
1. URL-encoded Base64
Base64 passed through a query string or cookie often arrives with =, + and / percent-encoded as %3D, %2B and %2F. Decode the URL encoding first (decodeURIComponent).
eyJ1c2VyIjoiYWRhIiwicm9sZSI6ImFkbWluIn0%3DeyJ1c2VyIjoiYWRhIiwicm9sZSI6ImFkbWluIn0=2. URL-safe Base64 given to a standard decoder
Base64URL (used by JWTs and many APIs) replaces + and / with - and _ and often drops padding. Java reports - as “Illegal base64 character 2d”. Use the URL-safe decoder (Base64.getUrlDecoder(), base64.urlsafe_b64decode) or translate the two characters.
PDw_Pz4-PDw/Pz4+3. A data URL or other prefix left in place
A data URL such as data:image/png;base64, is not part of the Base64 payload; neither is a label like Basic in an Authorization header. Strip everything up to and including the comma or space.
Basic YWRhOnMzY3JldA==YWRhOnMzY3JldA==4. A whole JWT decoded as one Base64 string
A JWT is three Base64URL segments joined by dots, and the dot is not a Base64 character. Decode each segment separately, or use a JWT decoder.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.abceyJzdWIiOiIxIn0Frequently asked questions
Are spaces and line breaks invalid in Base64?
MIME Base64 (email, PEM files) wraps lines at 76 characters, and most decoders, including atob and PasteKit, ignore whitespace. Strict decoders such as Python’s b64decode(validate=True) and Java’s basic decoder reject it.
What does "Illegal base64 character 2d" mean?
Java prints the offending character as a hex code: 2d is “-” and 5f is “_”, the URL-safe characters. 20 is a space and 25 is “%”, which points to URL encoding.
Why does Python sometimes say "Incorrect padding" instead?
Without validate=True, b64decode discards characters outside the alphabet and then finds that the remaining length is not a multiple of four. Missing = padding, common in Base64URL, causes the same message.