Intro #
I’m using Jellyfin version 10.11.4 and recently reorganized some media only to find that one of my libraries kept the old filepaths in the database as phantom entries. This meant these old entries were still visible even though they were now in a different library (which correctly picked them up).
Investigating #
These were things I tried that didn’t work:
- Individual Library Scan on ‘bad’ library
- Full Library Scan
- Individual Library Scan on ‘bad’ library and replace all metadata
- Remove ‘bad’ library, ran full library scan, and then re-added the library
- Ran multiple cleanup/etc jobs in the jellyfin admin panel
I then tried searching the internet and found a few github issues from 2024 or earlier, and also found this forum post from 2024 where theguymadmax mentions opening the TypedBaseItems table in the library.db. But I found that Jellyfin has since updated their database schema so the referenced table doesn’t exist. Before exploring the database further, I looked at the logs for clues and found this:
[INF] [41] Emby.Server.Implementations.Library.LibraryManager: Removing item, Type: "Series", Name: "mediaName", Path: "/libraryPath/mediaPath", Id: fce0bde8-b5d8-1d70-ce80-7ade30e313c7
[ERR] [41] Microsoft.EntityFrameworkCore.Database.Command: Failed executing DbCommand ("4"ms) [Parameters=["@__date_1='?' (DbType = DateTime), @__p_0='?' (Size = 1600)"], CommandType='Text', CommandTimeout='30']"
""UPDATE \"UserData\" AS \"u\"
SET \"ItemId\" = '00000000-0000-0000-0000-000000000001',
\"RetentionDate\" = @__date_1
WHERE \"u\".\"ItemId\" IN (
SELECT \"p\".\"value\"
FROM json_each(@__p_0) AS \"p\"
)"
[ERR] [41] Microsoft.EntityFrameworkCore.Query: An exception occurred while executing an 'ExecuteUpdate' operation for context type '"Jellyfin.Database.Implementations.JellyfinDbContext"'."
""Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 19: 'UNIQUE constraint failed: UserData.ItemId, UserData.UserId, UserData.CustomDataKey'.
From this error I found there was a constraint violation in the UserData table when the phantom Path was queried. Following the error entry I found the following:
at Jellyfin.Server.Implementations.Item.BaseItemRepository.DeleteItem(IReadOnlyList`1 ids)
at Emby.Server.Implementations.Library.LibraryManager.DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, Boolean notifyParentItem)
The prior database schema utilized a TypedBaseItems table but from the logs I knew I was looking for the BaseItem table and that the database was searching the Path. This gave what I needed to write a simple SQL script to remove these entries.
The Fix: Sqlite database update #
-
Install sqlite or an SQL client (I like DBeaver Community)
-
Turn off Jellyfin service/container
-
Navigate to Jellyfin’s
config/datafolder. For me that was in my docker mountjellyfin/config/data/ -
Create a backup of
jellyfin.db -
Open the database
-
sqlite:
sqlite3 jellyfin.dbSQLite version 3.51.1 2025-11-28 17:28:25 Enter ".help" for usage hints. sqlite> -
DBeaver: Create Connection -> Sqlite -> Select database and connect -> Once connected, right click database -> SQL Editor -> New SQL Script
-
-
Run a basic query for the faulty
BaseItem.Path.- Can add an
ORstatement if multiple paths were broken. - Keep the
%as a wildcard. - (Dbeaver: Ctrl+Enter to execute)
SELECT Path FROM BaseItems WHERE Path LIKE '/libraryPath/mediaPath%' OR Path LIKE '/libraryPath/mediaPath2%' OR Path LIKE '/libraryPath/mediaPath3%' OR Path LIKE '/libraryPath/mediaPath4%' OR Path LIKE '/libraryPath/mediaPath5%'; - Can add an
-
Review the output. This is a list of everything you’re about to delete from the database with the next command. If you need to adjust the library path(s) then repeat step 6 with another
SELECT Pathquery. -
Delete jellyfin phantom/ghost entries:
DELETE FROM BaseItems WHERE Path LIKE '/libraryPath/mediaPath%' OR Path LIKE '/libraryPath/mediaPath2%' OR Path LIKE '/libraryPath/mediaPath3%' OR Path LIKE '/libraryPath/mediaPath4%' OR Path LIKE '/libraryPath/mediaPath5%'; -
Save and Close Database
- sqlite (saved automatically):
.quit - Dbeaver: Ctrl+S to save, then close
- sqlite (saved automatically):
-
Turn on Jellyfin service/container
Conclusion #
You should now be able to review your library and verify the old ‘phantom’ media entries have been removed.