Base64 Decode "Invalid Character" Error: How to Fix
The Base64 "invalid character" error means the input string contains a character that isn't part of the Base64 alphabet. The most common causes are URL-safe characters, whitespace, missing padding, and input that isn't Base64 at all.
atob: invalid character → URL-safe Base64 (-,_) or whitespaceincorrect padding → Missing or extra = at endInvalid base64-encoded string → Python/Node version of the same errorsNon-base64 digit → Input is hex, plaintext, or a different encodingError 1: URL-safe Base64 Characters (- and _)
Standard Base64 uses + and /. URL-safe Base64 (Base64url) replaces them with - and _. JWT tokens always use Base64url. Standard decoders reject - and _ as invalid.
Error 2: Whitespace and Line Breaks
MIME Base64 (used in email) inserts a line break every 76 characters. If you copy Base64 from an email or PEM file, the line breaks cause "invalid character" errors.
Error 3: Incorrect Padding
Base64 output length must be a multiple of 4. If the encoded string is missing= padding, some decoders fail.
Note: URL-safe Base64 (JWTs) deliberately omits padding. Add it back before using standard atob().
Error 4: The String Is Not Base64
Sometimes the input isn't Base64 at all — it might be hex, a UUID, a raw password, or already decoded text.
| Looks like | It's actually | What to do |
|---|---|---|
| 2cf24dba5fb0a30e... | Hex (SHA-256 hash) | Use hex decode: Buffer.from(str, "hex") |
| 550e8400-e29b-41d4... | UUID | No decoding needed — it's already text |
| abc123!@#$ | Plain text or password | Do not decode — it's not encoded |
| %2B%2F%3D... | URL-encoded Base64 | URL-decode first: decodeURIComponent(str) |
| SGVsbG8= | Valid standard Base64 | Decode with atob() or equivalent |
Error 5: "atob is not defined" (Node.js)
atob() and btoa() are browser APIs. They were added to Node.js in v16 (global) and are stable in v18+. For older Node.js or guaranteed compatibility, use Buffer:
Debug Checklist
Frequently Asked Questions
Why does my JWT fail to decode with atob()?
JWTs use Base64url (URL-safe Base64) which replaces + with - and / with _ and omits = padding. Standard atob() rejects - and _ as invalid characters. To decode a JWT payload: const parts = token.split("."); const payload = JSON.parse(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"))). Or use a JWT library which handles this automatically.
What is the valid Base64 character set?
Standard Base64 (RFC 4648): A-Z (26), a-z (26), 0-9 (10), + and / (2) = 64 characters, plus = for padding. URL-safe Base64: same but - replaces + and _ replaces /. Characters outside this set (spaces, newlines, !, @, #, etc.) are invalid and will cause decode errors.
How do I check if a string is valid Base64?
Test with a regex: /^[A-Za-z0-9+/]*={0,2}$/.test(str) for standard Base64. Or simply attempt to decode and catch the error. Note that some strings pass the regex but decode to unexpected data if the padding is wrong.
Can Base64 decode the wrong data silently?
Yes. If padding is wrong or the string has extra characters, some lenient decoders may silently produce garbage output instead of throwing an error. Python's base64.b64decode() with validate=False will skip invalid characters. Always use validate=True (or the equivalent strict mode) in security-sensitive contexts.
Key Takeaways
- "Invalid character" usually means URL-safe Base64 (-,_) or whitespace in the string.
- Always strip whitespace before decoding MIME Base64 from emails or PEM files.
- JWTs use Base64url — replace - with + and _ with / before using atob().
- In Node.js, use Buffer.from(str, 'base64') instead of atob() for full compatibility.
- If "incorrect padding": add = characters until length is a multiple of 4.