Errores comunes con Base64: caracteres no válidos, relleno incorrecto, texto ilegible y archivos rotos
Base64 es tan sencillo que sus fallos rara vez parecen fallos de Base64. Parecen un inicio de sesión que falla, una imagen que no se muestra, un nombre con tilde convertido en `é` o una firma que nunca coincide. La codificación casi siempre hizo exactamente lo que se le pidió; el error está en lo que recibió o en cómo se leyó el resultado. Esta lista está ordenada por síntoma para ir de lo que ves a la causa.
"Carácter no válido" al decodificar
El decodificador encontró un carácter fuera del alfabeto Base64. Los culpables habituales, por orden:
Base64url en un decodificador normal: - y _ en lugar de + y /. Los JWT, los valores PKCE de OAuth y muchos tokens de URL usan Base64url. Cambia los caracteres (-→+, _→/) o usa un decodificador con opción Base64url.
Un prefijo data: todavía pegado, por ejemplo data:image/png;base64,iVBOR…. Elimina todo hasta la coma incluida.
Espacios y saltos de línea copiados de un correo, un archivo PEM o una terminal. La mayoría de los decodificadores los rechazan; quítalos antes.
+ convertidos en espacios por la decodificación de una URL. Un valor Base64 enviado en una query string sin codificar llega como ab cd en lugar de ab+cd.
// normalizar antes de decodificar
const limpio = entrada
.replace(/^data:[^,]*,/, '') // prefijo data URI
.replace(/\s+/g, '') // espacios y saltos de línea
.replace(/-/g, '+').replace(/_/g, '/'); // Base64url -> Base64
Errores de relleno (padding)
Python lanza binascii.Error: Incorrect padding, y otros decodificadores fallan de forma parecida, cuando la longitud no es múltiplo de cuatro. Base64url y las partes de un JWT suelen omitir el =, así que hay que restaurarlo.
import base64
t = 'eyJzdWIiOiIxIn0' # longitud 15: falta un '='
base64.urlsafe_b64decode(t + '=' * (-len(t) % 4)) # b'{"sub":"1"}'
Una longitud con resto 1 al dividir entre 4 no se arregla con relleno: ningún Base64 válido tiene esa longitud. Se perdieron caracteres, normalmente por el límite de una columna, una línea de log cortada o una copia incompleta.
El texto decodificado sale ilegible
Base64 restaura los bytes exactos. Si el texto se ve mal, los bytes se convirtieron a texto con otra codificación, al codificar o al decodificar.
"café" en bytes UTF-8 -> Y2Fmw6k=
decodificado como UTF-8 -> café correcto
decodificado como Latin-1 -> café el clásico mojibake
"café" en bytes Latin-1 -> Y2Fm6Q== (otro string para el mismo texto)
En el navegador la causa suele ser btoa() y atob(), que trabajan con "strings binarios" Latin-1. btoa('café') funciona por casualidad, porque la é existe en Latin-1, pero produce bytes Latin-1, y btoa('😀') lanza InvalidCharacterError. Codifica el texto con TextEncoder y decodifícalo con TextDecoder.
En PowerShell, [Text.Encoding]::Unicode es UTF-16LE, no UTF-8. El Base64 de un texto UTF-16 ocupa el doble y, decodificado en otro sistema, muestra un carácter nulo detrás de cada letra. Usa [Text.Encoding]::UTF8.
Imágenes o PDF corruptos tras decodificar
Una imagen que no abre, un PDF "dañado" o un ZIP con errores después de pasar por Base64 casi siempre indican que los bytes pasaron por una conversión de texto en algún punto.
Leer un archivo binario como texto, por ejemplo con fs.readFileSync(ruta, 'utf8') o open(ruta) sin 'rb', sustituye las secuencias UTF-8 no válidas por EF BF BD antes de codificar, y el daño ya está hecho. Lee y escribe siempre los binarios como bytes, y guarda el resultado con escritura binaria ('wb', Buffer, Blob).
Comprueba el resultado por sus bytes mágicos: un PNG empieza por 89 50 4E 47, un JPEG por FF D8 FF, un PDF por %PDF y un ZIP (incluidos DOCX y XLSX) por PK. Si el inicio es correcto y aun así falla, compara tamaños: más corto indica recorte y más largo, conversión de texto.
Saltos de línea y el salto final
El base64 de GNU corta la salida cada 76 caracteres y los archivos PEM cada 64. Un resultado con saltos insertado en un data URI, un string JSON, una cabecera HTTP o una variable de entorno la rompe. Usa base64 -w0 en Linux u openssl base64 -A.
echo añade un salto de línea, así que echo 'secreto' | base64 codifica secreto\n. Una contraseña o clave de API construida así falla al autenticar. Usa printf '%s'. El final lo delata: según la longitud del texto, el salto de línea codificado aparece como Cg==, como o= precedido de otra letra o como una K final.
echo 'secreto' | base64 # c2VjcmV0bwo= <- incluye \n
printf '%s' 'secreto' | base64 # c2VjcmV0bw==
Doble codificación y Base64 de lo que no toca
Codificar un valor ya codificado produce un string válido pero inútil: al decodificar una vez sale otro Base64. Pasa cuando una librería codifica los campos binarios automáticamente y quien la llama ya los había codificado.
Un error parecido es codificar la representación hexadecimal de un hash en lugar de sus bytes. SHA-256 en hexadecimal son 64 caracteres, y su Base64 son 88. Lo esperado, el Base64 de los 32 bytes, son 44. Subresource Integrity, la cabecera HTTP Digest y muchas firmas esperan esto último.
Tratar Base64 como protección
Base64 no oculta nada. Una contraseña guardada en Base64, una clave de API "ofuscada" en una app móvil o un Secret de Kubernetes que todo el equipo puede leer son texto plano para cualquiera que mire. Los escáneres de secretos y los atacantes decodifican Base64 automáticamente. Cifra lo que deba ser confidencial y guarda las contraseñas con Argon2id o bcrypt.
Fuentes y lecturas recomendadas
RFC 4648 define Base64, Base64url y el relleno; RFC 2045 las líneas de 76 caracteres de MIME y RFC 7468 las de 64 de PEM. MDN documenta btoa(), atob(), TextEncoder y su limitación con Unicode. La especificación Subresource Integrity del W3C define los resúmenes en Base64.
Frequently asked questions
¿Por qué falla la decodificación de Base64 con un carácter no válido?
La entrada contiene caracteres fuera del alfabeto: normalmente `-` y `_` de Base64url, un prefijo `data:`, espacios o saltos de línea, o espacios donde había `+` que se perdieron en una URL. Limpia o convierte la entrada antes de decodificar.
¿Cómo arreglo el error de relleno (padding) en Base64?
Añade `=` hasta que la longitud sea múltiplo de 4, algo habitual en Base64url y JWT. Si la longitud entre 4 da resto 1, el string está recortado y no se puede reparar con relleno.
¿Por qué btoa lanza InvalidCharacterError?
Porque `btoa()` trata cada carácter como un byte y solo acepta valores hasta U+00FF: un emoji o una letra de otro alfabeto lo hacen fallar. Convierte antes el texto con `new TextEncoder().encode()` y codifica esos bytes, o usa `Buffer` en Node.js.
¿Por qué mi imagen o PDF decodificado está corrupto?
Los datos binarios se trataron como texto en algún paso, lo que sustituye secuencias de bytes. Lee los archivos como bytes antes de codificar, decodifica a bytes y guárdalos con escritura binaria.
¿Por qué mi Base64 termina en Cg== o en o=?
Suele ser la codificación de un salto de línea final añadido por `echo`. Usa `printf '%s'` o `echo -n` para que no se codifique.
¿Base64 sirve para proteger contraseñas o claves?
No. Cualquiera puede revertirlo al instante. Usa cifrado real para los datos confidenciales y un algoritmo de hash de contraseñas como Argon2id para las contraseñas.