Skip to content

SQLite→PostgreSQL migration drops applied code-migration history; Jellyfin 12.1 fails with 'no such table: TypedBaseItems' #9

Closed

Description

@jasimancas

Summary

On a fresh Jellyfin 12.1 install, running Migration → SQLite → PostgreSQL and then Activate PostgreSQL leaves Jellyfin unable to start:

SQLite Error 1: 'no such table: TypedBaseItems'.
   at Jellyfin.Server.Migrations.Routines.RemoveDuplicateExtras.Perform()

Right before it, Jellyfin also logs:

Cannot make a backup of "library.db" at path ".../library.db" because file could not be found at ...

The legacy migration RemoveDuplicateExtras should already be marked as applied, but it runs again and opens an (empty) library.db, which has no TypedBaseItems table.

Environment

  • Plugin: 3.0.3
  • Jellyfin: 12.1 (official jellyfin/jellyfin:12.1 image, derived image with postgresql-client-17)
  • PostgreSQL: 17 (official postgres:17 image)
  • Docker (Unraid), Jellyfin config on a bind mount

Steps to reproduce

  1. Fresh Jellyfin 12.1 container, complete the setup wizard, restart.
  2. Install plugin 3.0.3, restart.
  3. Configure the connection, run Migration → SQLite → PostgreSQL, then Activate PostgreSQL (restart).
  4. Jellyfin fails to start with the error above.

What I found

  • jellyfin.db (SQLite source) has 102 rows in __EFMigrationsHistory. All 102 ids have exactly 14 digits before the underscore (e.g. 20250420080000_RemoveDuplicateExtras); none has 15.
  • After the migration, PostgreSQL's public."__EFMigrationsHistory" had only 12 rows (I did not capture which ones).
  • MigrationCodeMigrations.IsCodeMigrationId requires id[15] == '_', i.e. 15 digits before the underscore. With 14-digit ids the underscore is at index 14, so the filter rejects every one of them and CopyAppliedAsync copies no code migrations from SQLite. The 12 rows already in PostgreSQL would then be unrelated to that copy (likely the provider's own schema migrations). I have not checked the plugin log line [CodeMigrations] N migraciones aplicadas copiadas; this is my reading of the code plus the data above.
  • I copied every row of the SQLite __EFMigrationsHistory into PostgreSQL manually (INSERT ... ON CONFLICT DO NOTHING; 104 rows afterwards, all 14-digit ids) and restarted. The first restart still failed with the same error. I restarted again without changing anything else and Jellyfin started with PostgreSQL active. So the full history seems to be what was missing, but I cannot explain why the first restart after the insert still failed.
  • The plugin log of the migration run only lists the table copy (59 rows, 31 tables) and has no line about the migration history.

Expected

After the SQLite → PostgreSQL migration, the code migrations already applied in the SQLite history are present in PostgreSQL, so Jellyfin does not re-run legacy routines that need library.db.

Suggestion

Make IsCodeMigrationId accept the 14-digit form that Jellyfin 12.1 actually writes (and since provider schema ids also have 14 digits, distinguish them by another rule, e.g. skip ids known to belong to the plugin's own EF migrations), and consider a post-migration check that warns when the number of copied history rows is much lower than the source.

Happy to provide more logs or test a fix.

Activity

  1. self-assigned this
    on Oct 17, 2026
  2. BORNIOS commented on Oct 17, 2026

    @BORNIOS
    Owner

    Thanks for the detailed report — the symptom and the expected behaviour are exactly right, and copying the missing history rows is what the plugin now does automatically.

    One correction on the mechanism, because it changes the fix: the filter you found was dead code. The copy step was disabled in 3.0.2, so CopyAppliedAsync was never invoked during the migration. On top of that, the filter itself could not have worked: "yyyyMMddHHmmsss" renders 14 digits in .NET, not 15, so id[15] == '_' never matches. Accepting 14 digits would not be safe either — the provider's schema migrations share the same shape, and copying them into PostgreSQL's history would make EF skip migrations that never ran.

    What changed (PR #11):

    • Only ids whose name belongs to a type carrying JellyfinMigrationAttribute are copied, so Jellyfin's routines are carried over and schema migrations are left to EF.
    • The log now reports how many rows were copied and warns when none were, which is the sanity check you suggested.
    • The unused 15-digit filter is gone.

    Verified with unit tests for the id matching and integration tests against PostgreSQL 17 that copy a routine and deliberately ignore a schema migration. If you can test a fresh SQLite → PostgreSQL migration with the next build, that would confirm it end to end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions