"Database disk image is malformed"

Es el código de error 11, SQLITE_CORRUPT: al recorrer un b-tree, SQLite llegó a una página o un puntero que ya no tiene sentido, así que se niega a confiar en el archivo. Las filas en sí casi siempre siguen en el disco — lo que se rompió es el mapa que lleva a ellas — y alcanzarlas no exige enviar tu base de datos a ningún sitio.

Tu archivo nunca sale de tu dispositivo — la reparación ocurre en tu navegador. 0 bytes subidos.

Your data is the big block — usually intact. What breaks is the small index. Repair rebuilds it.

Suelta un archivo aquí o exploraToca para elegir un archivo

ZIP · Office · PDF · vídeo · JPG · PNG · RAR/7z · SQLite — reparados aquí mismo, en tu navegador. No se sube nada

0 bytes subidosGratis: 3 reparaciones/día — hasta 500 MB de vídeo, 100 MB de documentos y archivos, 50 MB de fotos. Descarga gratuita. Una reparación 5,90 €. Sin cuenta.

Repáralo ahora

  1. Suelta el archivo
  2. La reparación ocurre en local
  3. Descarga el resultado

Cuando SQLite — o cualquier cosa construida sobre él: el historial de un navegador, una copia de seguridad de un móvil, la base de datos local de una app de mensajería, los ajustes de una aplicación de escritorio — informa de "database disk image is malformed", ha topado con SQLITE_CORRUPT (código de resultado 11). Estaba recorriendo el b-tree que indexa tus tablas y aterrizó en una página cuyo byte de tipo, punteros de celda o cabecera de registro contradicen el formato, así que se detiene en lugar de devolver basura. Suelta el archivo .db, .sqlite o .sqlite3 arriba y la herramienta lo lee página a página en tu navegador, sigue la estructura de b-tree que sobreviva, barre el resto con un escaneo sin punteros y escribe cada fila decodificable en una base de datos nueva y abrible — sin que el archivo salga nunca de tu equipo.

Qué significa "malformed" a nivel de página

Una base de datos SQLite es un único archivo cortado en páginas de tamaño fijo — una potencia de dos entre 512 y 65536 bytes, almacenada como un valor de 16 bits en el desplazamiento 16 de la cabecera (el valor en disco 1 es la forma que tiene la especificación de representar 65536). Todo archivo empieza con la cadena mágica de 16 bytes SQLite format 3\0, seguida de una cabecera de 100 bytes que registra el tamaño de página, la codificación de texto en el desplazamiento 56 (1 = UTF-8, 2 = UTF-16le, 3 = UTF-16be), el número de páginas declarado en el desplazamiento 28 y la lista de libres (freelist). Si el tamaño de página se lee mal, cada página posterior a la primera cae en el desplazamiento equivocado — y esa es una de las formas en que un archivo queda "malformed" sin que se toque una sola fila.

Dentro, los datos viven en b-trees. Cada página empieza con un byte de tipo: 0x0d para una hoja de tabla (que contiene las filas reales), 0x05 para una interior de tabla (que solo contiene punteros a páginas hijas) y 0x0a/0x02 para los equivalentes de índice. La página 1 es especial — su cabecera de b-tree va después de la cabecera de base de datos de 100 bytes — y es la raíz de sqlite_master, la tabla de esquema cuyas filas son (type, name, tbl_name, rootpage, sql). Para leer una de tus tablas, SQLite busca ahí su rootpage, luego desciende por las páginas interiores hasta las hojas, leyendo de cada celda la longitud de carga (varint), el rowid (varint) y el cuerpo del registro.

Como una consulta recorre este árbol, un solo enlace roto envenena todo lo que hay por debajo. Una página interior puesta a ceros, un puntero de celda que apunta más allá del final de la página, un puntero al extremo derecho que forma un bucle, o una cabecera de registro cuyos tipos serie no cuadran — cualquiera de estos hace que SQLite lance "database disk image is malformed" en cuanto pisa el daño. Las filas de las páginas hoja sanas están intactas; SQLite simplemente no puede alcanzarlas a través de un índice roto. PRAGMA integrity_check informa de esta misma clase de fallo.

Cómo un recorrido de páginas en crudo rescata tus filas

La solución imita lo que hace el propio comando .recover del shell de SQLite, pero se ejecuta enteramente en el navegador. Primero la herramienta lee sqlite_master desde la página 1 para conocer el nombre de cada tabla, sus columnas (extraídas del SQL de su CREATE TABLE) y su rootpage, y luego recorre cada b-tree desde esa raíz — la vía rápida y exacta cuando los punteros están intactos. Cuando un recorrido topa con una página que falta o es inválida, degrada a "incompleto" en vez de fallar, conservando todas las filas que reunió hasta la ruptura.

Después llega la parte que supera a un simple reintento de apertura: un barrido sin punteros. El escáner ignora todos los punteros y trata cada página hoja de tabla (tipo 0x0d) del archivo como una bolsa de registros, decodificando cada celda directamente de sus bytes — la longitud de la cabecera (varint), el array de tipos serie y luego cada valor (el tipo serie 0 es NULL, 8/9 son los literales 0 y 1, 7 es un float64, y los códigos pares/impares por encima de 12 son BLOB/TEXT). Las cadenas de desbordamiento (overflow) de los valores grandes se siguen de página en página. Así es exactamente como sobreviven las filas a una página interior puesta a ceros, a una página en la freelist o a una tabla eliminada: la hoja sigue conteniendo los datos aunque ya nada apunte a ella. Las filas cuyo número de columnas coincide de forma inequívoca con una tabla conocida se reinsertan en ella; las que de verdad no se pueden atribuir se escriben en una tabla lost_and_found con columnas pgno, cellidx, nfield, rowid y c0…cN, de modo que no se descarta nada decodificable.

Por último la herramienta construye una base de datos nueva con sqlite3.wasm (cargado desde el mismo origen en /engines/sqlite/), emitiendo un CREATE TABLE permisivo para cada tabla — se conservan los nombres originales de las columnas y cualquier INTEGER PRIMARY KEY para que el alias del rowid se mantenga, pero sin restricciones NOT NULL/UNIQUE/CHECK/clave foránea que rechazarían una fila resucitada — y luego hace INSERT OR IGNORE de cada fila recuperada y exporta el resultado como un .db limpio. Si se aporta un archivo hermano -wal (registro de escritura anticipada), sus tramas confirmadas se superponen primero, de forma que la recuperación refleja la última transacción confirmada.

Dónde se detiene de verdad el dato — y por qué no se sube nada

La recuperación alcanza lo que está presente; no puede inventar lo que el disco ya no guarda. Si el archivo quedó truncado — una copia o sincronización que se detuvo antes de tiempo, dejando un tamaño que no es un múltiplo exacto del tamaño de página — toda página posterior al corte ha desaparecido físicamente, y obtienes las filas que se escribieron por completo antes de él. Las celdas que están en páginas intactas pero cuya cabecera de registro es incoherente consigo misma (el clásico bit-rot) se descartan y se cuentan, en lugar de adivinarlas. Una base de datos cifrada (SQLCipher o la extensión SEE) no tiene magia en texto plano — su primera página es texto cifrado — así que sin la clave no hay nada que recorrer, y no se intenta la recuperación.

Dos advertencias de honestidad en las que el propio SQLite insiste. Las filas eliminadas pueden reaparecer: el escaneo sin punteros lee páginas de la freelist y sin compactar (vacuum), así que registros que creías desaparecidos pueden resurgir en el resultado. Y los valores recuperados reflejan las clases de almacenamiento en disco, no las afinidades de columna declaradas — el tipo aparente de una columna puede cambiar. La propia guía de SQLite es tajante: los datos extraídos de una base de datos corrupta son siempre sospechosos. Verifícalos contra una fuente fiable antes de confiar en ellos.

Todo esto se ejecuta en la pestaña de tu navegador — el escáner es TypeScript sin dependencias y el escritor es WASM, así que no hay ida y vuelta a un servidor. Eso importa cuando la base de datos es un gestor de contraseñas, un historial de chat, datos de salud o el almacén privado de una app: puedes abrir la pestaña Red y confirmar que nunca salen 0 bytes del archivo de tu equipo.

Qué puede y qué no puede reparar

Puede reparar

  • Bases de datos que lanzan SQLITE_CORRUPT / "database disk image is malformed" por una página o un puntero de b-tree dañado
  • Filas en páginas hoja de tabla sanas que una página interior rota o un rootpage defectuoso dejaron inaccesibles
  • Filas en páginas interiores puestas a ceros, páginas en la freelist o tablas eliminadas, recuperadas por el escaneo de hojas sin punteros
  • Valores grandes que se derramaron en cadenas de páginas de desbordamiento, reensamblados durante el recorrido
  • Cambios sin checkpoint en un archivo hermano -wal, superpuestos antes de escanear

No puede reparar

  • Filas posteriores al corte en un archivo truncado — esas páginas han desaparecido físicamente
  • Celdas cuya cabecera de registro está demasiado dañada para decodificarse (bit-rot) — se descartan, no se adivinan
  • Bases de datos cifradas SQLCipher/SEE sin la clave (las páginas son texto cifrado)
  • Las garantías originales UNIQUE/CHECK/clave foránea — la recuperación relaja las restricciones para que las filas resucitadas no se rechacen
  • Un archivo de 0 bytes o sin la magia de SQLite ni páginas hoja decodificables

Si una reparación falla, te decimos por qué (datos que faltan frente a estructura dañada), y nunca se te cobra por una reparación fallida.

Preguntas frecuentes

¿"Database disk image is malformed" significa que he perdido mis datos?

Normalmente no. Significa que SQLite siguió el b-tree hasta una página o un puntero que viola el formato y se detuvo — código de error 11, SQLITE_CORRUPT. Las filas de las páginas hoja sanas siguen en el disco; solo son inaccesibles a través del índice roto. Leer las páginas hoja directamente y reconstruir una base de datos nueva recupera la mayoría.

¿En qué se diferencia esto de reabrir el archivo o ejecutar PRAGMA integrity_check?

Reabrir falla de la misma forma, e integrity_check solo informa del daño. La recuperación, en cambio, recorre el b-tree donde puede y, sobre todo, hace un escaneo sin punteros de cada página hoja de tabla — así rescata filas en páginas a las que ya nada apunta, algo que una apertura normal jamás puede alcanzar.

¿Qué es la tabla lost_and_found en la base de datos recuperada?

Es donde se guardan, en lugar de descartarse, los registros que no se pudieron asociar a una tabla conocida. Cada uno se almacena con su número de página de origen (pgno), índice de celda, número de campos y rowid, más sus valores de columna en crudo como c0, c1, etc. — para que puedas inspeccionarlos y reubicarlos a mano.

¿Por qué aparecieron filas eliminadas tras la recuperación?

El escaneo sin punteros lee páginas de la freelist y sin compactar (vacuum), donde SQLite deja los registros antiguos hasta que se reutiliza el espacio. La recuperación prioriza la exhaustividad, así que las filas previamente eliminadas pueden resurgir. Trata cualquier dato recuperado como sospechoso y verifícalo contra una fuente fiable.

¿Se sube mi base de datos para recuperarla?

No. El archivo se lee desde tu disco y se reconstruye en la pestaña de tu navegador; el escáner de páginas es TypeScript puro y el escritor es sqlite3.wasm servido desde el mismo origen. Puedes observar la pestaña Red y confirmar que salen 0 bytes — lo cual importa para historiales de chat, gestores de contraseñas, copias de seguridad y datos de salud.

Relacionado: Reparar una base de datos SQLite · Reparar un libro de Excel · "The file is damaged and could not be repaired" · Verifica que no se sube nada