Match your error message to the fix
Every runtime words a Base64 failure differently. Find your error in the left column. The middle column names the usual cause, the right column names the switch above for the job.
| Error you got | Usual cause | Switch on this page |
|---|---|---|
Browser atob(): InvalidCharacterError | A data: header, wrapping quotes, or -_ from a JWT. Line breaks pass, atob() skips whitespace. | Headers, prefixes. URL-safe alphabet. |
Python: binascii.Error: Incorrect padding | Trailing = dropped by a URL, cookie, or JWT | Padding |
Java: Illegal base64 character 20 | Hex 20 is a space. A + went through a query string parser and came out as a space. | Spaces were + signs |
Go: illegal base64 data at input byte 0 | The byte at the reported offset sits outside the alphabet. Offset 0 usually means a quote. Go skips \r and \n on its own, so spaces, backslashes, or - from base64url are the usual suspects. | Quotes, JSON escapes. URL-safe alphabet. |
PHP: base64_decode($s, true) returns false | Same causes as Go. Without strict mode PHP skips junk silently and hands back shifted bytes. | Characters outside the alphabet |
| No error, broken image or garbled text | An = in the middle from two joined strings, or a cut-off tail | Padding mid-string. Read the truncation warning. |
The sample on this page, step by step
Click Wrapped email body above. The string is a usage invoice for toolexe.com, copied out of a raw email. Mail clients wrap Base64 at 76 characters. This copy also picked up four spaces of indent per line. The == at the end got lost too.
- The log reports 2 line breaks plus 12 spaces removed. Three lines, four spaces each.
- 214 characters remain. 214 divided by 4 leaves 2, so 2 padding characters go on the end.
- The decoded check reads 160 bytes of UTF-8 text and prints the invoice, ending with Wajahat Qasim's billing address.
Next, click + lost in a URL. The string decodes without any error, but into 30 bytes of noise. The blue hint explains why. One click on Treat spaces as + signs brings back the 32-byte HMAC-SHA256 reset token. Most fixers strip those spaces and stop, which hands you a decodable, wrong value.
Why the fixes run in a fixed order
The checklist runs top to bottom. Two pairs only work in sequence:
- Percent escapes before the alphabet filter. Filter first and
%2Bloses its%, leaving the letters2B. Both sit inside the Base64 alphabet, so the string decodes cleanly into the wrong bytes. - Spaces to + before whitespace removal. Strip first and a 44-character token drops to 41. Padding fills the gap. Nobody notices until a signature check fails.
After cleanup, the page decodes to bytes and encodes those bytes again. The output is canonical: the same bytes always give the same string. The output menu picks the flavor. Use URL-safe without padding for JWT segments, MIME for email bodies, PEM for certificates and keys.
How the page spots a cut-off string
Padding makes a truncated string decodable. The missing bytes stay missing. To catch this, the page reads the leftover bits in the last character. Every clean encoder sets those bits to zero. Nonzero bits mean the string stopped partway through a byte group, and the log raises a warning.
The check catches about two cut-offs in three. When the cut lands on a 4-character boundary the bits look clean. Only the content gives the problem away: JSON missing its closing brace, a PNG missing its IEND chunk, a gzip stream failing its CRC. Load Cut-off payload to see both warnings fire on one string.
The same repair in your own code
Once the log names the cause, fix the producer. Where a decoder has to tolerate the damage, these handle padding plus the URL-safe alphabet.
Browser JavaScriptconst fix = s => {s = s.replace(/\s+/g, '').replace(/-/g, '+').replace(/_/g, '/');return s + '='.repeat((4 - s.length % 4) % 4);};const bytes = Uint8Array.from(atob(fix(input)), c => c.charCodeAt(0));Pythonimport base64, re
s = re.sub(r'\s+', '', s)data = base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))PHP$s = strtr(preg_replace('/\s+/', '', $s), '-_', '+/');$bytes = base64_decode($s . str_repeat('=', (4 - strlen($s) % 4) % 4), true);Gos = strings.TrimRight(strings.NewReplacer("+", "-", "/", "_").Replace(s), "=")b, err := base64.RawURLEncoding.DecodeString(s)Buffer.from(s, 'base64') and Python's b64decode() without validate=True both skip characters outside the alphabet with no error. Feed them dG9v*bGV4ZQ and you get toolexe back. Convenient, until a stray character in binary data shifts every byte after it. Validate first when the bytes matter.When repair is the wrong move
- Signed or encrypted values. JWT signatures, HMAC tokens, AES ciphertext. A repaired string decodes, but if a fix guessed wrong, the signature check fails. The failure is correct. Pull a fresh copy from the source instead of forcing a guess through.
- Heavy removal. One stray
*from a Markdown paste is harmless. Dozens of removed characters means a different encoding: Base32 (A to Z plus 2 to 7), Base58 from crypto wallets, Ascii85 from PDF streams. Run the string through the Base64 detector first. - Hex strings. Every hex digit sits inside the Base64 alphabet, so a hex string decodes without complaint into meaningless bytes. If the decoded check says binary data where you expected text, look at the input again.
- Big files. Everything runs inside your browser tab. Strings past a few tens of megabytes make the tab sluggish. Use
base64 -don Linux or macOS, orcertutil -decodeon Windows, for those.
Nothing you paste leaves your browser. Repair, decoding, file type detection, previews all run in JavaScript on this page, so no server sees a token or key you test here.
