Jellyfin v10 to v12

What a nightmare upgrading and trying to fix SQLite errors for Jellyfin

Here is a small sample of me going through the motions on how to get this going again.

I ran a separate Debian container to poke around with SQL. The following is my conversation with Claude... While my thoughts on AI are mixed, this actually repaired my database.


1. SQL container:

bash

docker compose stop jellyfin

docker run --rm -it -v ./config/data:/data debian:bookworm-slim bash -c "apt-get update && apt-get install -y sqlite3 && bash"
cd /data

2. Find every column across the entire database that might hold a date, and check each for this exact corruption pattern (character 14 is 2 instead of :):

bash

sqlite3 jellyfin.db "SELECT m.name AS tbl, p.name AS col FROM sqlite_master m JOIN pragma_table_info(m.name) p WHERE m.type='table' AND p.name LIKE '%Date%';" > /tmp/date_columns.txt
cat /tmp/date_columns.txt

3. Run this loop to both count and fix every one of those columns in one pass:

bash

while IFS='|' read -r tbl col; do
  count=$(sqlite3 jellyfin.db "SELECT COUNT(*) FROM \"$tbl\" WHERE SUBSTR(\"$col\",14,1) = '2' AND LENGTH(\"$col\") >= 19;")
  if [ "$count" != "0" ]; then
    echo "Fixing $count rows in $tbl.$col"
    sqlite3 jellyfin.db "UPDATE \"$tbl\" SET \"$col\" = SUBSTR(\"$col\",1,13) || ':' || SUBSTR(\"$col\",15) WHERE SUBSTR(\"$col\",14,1) = '2' AND LENGTH(\"$col\") >= 19;"
  fi
done < /tmp/date_columns.txt

4. Verify nothing's left:

bash

while IFS='|' read -r tbl col; do
  sqlite3 jellyfin.db "SELECT '$tbl.$col: ' || COUNT(*) FROM \"$tbl\" WHERE SUBSTR(\"$col\",14,1) = '2' AND LENGTH(\"$col\") >= 19;"
done < /tmp/date_columns.txt

All should say : 0.

5. Final integrity check, then exit and restart:

bash

sqlite3 jellyfin.db "PRAGMA integrity_check;"
exit
docker compose up

This should clear out every remaining instance of this specific corruption in one shot rather than the migration finding them 10,000 rows at a time.

That said — if you'd rather just cut your losses, starting over is a completely reasonable call, especially after this many rounds. Here's what that actually looks like, and the honest tradeoff: