Repository navigation
[bug] A tagged movie folder is merged into a different movie whose TMDB and IMDb IDs don't match its tags #2263
Description
What happened
A movie folder tagged with its own TMDB and IMDb IDs gets attached to a different movie whose TMDB and IMDb IDs both differ from the tags. The tagged movie gets no catalog item (GET /api/v2/catalog/items/movie-tmdb-<tag> returns 404), and its file shows up as a second version of the other movie. For example, Batman (1943) is missing, and "Bataan (1943)" lists two versions, one of which is the Batman file.
Reproduced on demand, 1 of 1 attempt (2026-10-11, build 1258 · 3f171de). The AI agent named in the disclosure ran the admin API calls, with my approval. It detached the Batman file from "Bataan" without saving an override. The split's own rescan picked up the folder, and 27.7 s after the rescan row the server put the file back in "Bataan" and logged metadata: rebound skeleton to existing item. This ran on the state the first merge left behind; see Technical notes for why that matters.
First logged 2026-10-10, same build. A scan of library "Movies" started at 23:00:01 UTC and reached its new Archive folder (4,124 files) at 23:02:10. In the scanner rows I exported, Archive is missing from the 13:16–13:17 scan and appears with 0 files at 13:23:18. Between 23:03 and 23:20 the scan logged metadata: rebound skeleton to existing item 5 times. Those are all of that message's rows before the reproduction:
New folder in library 1 Archive |
Now a version of | The receiving movie's other file |
|---|---|---|
Batman (1943) [imdb-tt0035665][tmdb-125249] |
Bataan (1943), tmdb 43506, imdb tt0035664 | library 6 "Movies-NotRated", added 2026-08-30 |
Broadway (1929) [imdb-tt0019725][tmdb-130717] |
The Broadway Melody (1929), tmdb 65203, imdb tt0019729 | library 6, added 2026-08-30 |
His and Hers (1961) [imdb-tt0054973][tmdb-287128] |
Rocco and His Brothers (1960), tmdb 8422, imdb tt0054248 | library 6, added 2026-08-30 |
The Traitors (1962) [imdb-tt0057599][tmdb-299287] |
The Counterfeit Traitor (1962), tmdb 43013, imdb tt0055871 | library 6, added 2026-08-30 |
Monk (1968) [imdb-tt1864420][tmdb-595980] |
Monk in Europe (1968), tmdb 595983, imdb tt1864421 | library 1 Archive, added in the same scan 0.43 s after the Monk file; the merge came 1.7 s later |
- For all five new folders,
movie-tmdb-<tag>returns 404. - Library roots lists the Batman and Monk folders as
resolvedwith their tagged IDs and"override_source": "none", and the same for Bataan's own folder in library 6. - Each receiving movie's TVDB ID resolves on thetvdb.com to that movie itself (Bataan's 66484 →
/movies/bataan, and so on). - TMDB lookups through the server (
POST /api/v2/admin/items/{id}/match/searchwithtmdb_id) return, for each of the 10 movies, its own IMDb ID and no TVDB ID. - Titles and years of the pairs: Batman / Bataan (1943 / 1943), Broadway / The Broadway Melody (1929 / 1929), His and Hers / Rocco and His Brothers (1961 / 1960), The Traitors / The Counterfeit Traitor (1962 / 1962), Monk / Monk in Europe (1968 / 1968).
What still works: Monk in Europe, also new in that scan, got its own item. Library roots reads the tags correctly. I didn't check how the other Archive files matched, beyond these five log rows.
Older merges in library "Movies-4K" may be related. That's how I noticed the problem: I couldn't find Spider-Man: No Way Home in the web app or the Android app, because its files were inside "3 Way". Those cases look different and have no logs, so I'll add them in a comment.
Steps to reproduce
This was done on my server. I haven't tried it on a clean server; an untested idea for that is under Technical notes.
Starting point (2026-10-11, build 1258 · 3f171de):
- Library 1 "Movies" (folders
[MOVIES-ROOT-1]/1980…/2020and[MOVIES-ROOT-1]/Archive) and library 6 "Movies-NotRated" (one sibling folder under[MOVIES-ROOT-1]) are both of type movies, with metadata providers TMDB, MDBList and NFO in that order. - "Bataan (1943)" (
movie-tmdb-43506: tmdb 43506, imdb tt0035664, tvdb 66484) has two versions:- file 80798 in library 6, folder
Bataan (1943) [imdb-tt0035664][tmdb-43506]; - file 1298283 in library 1,
[MOVIES-ROOT-1]/Archive/Batman (1943) [imdb-tt0035665][tmdb-125249]/Batman (1943) [imdb-tt0035665][tmdb-125249][DVD][AC3 2.0][x264].mkv. The 2026-10-10 merge attached it.
- file 80798 in library 6, folder
- I haven't checked whether the Batman, Bataan, Monk or Monk in Europe folders contain
.nfofiles.
- With an admin session, detach the Batman file without saving an identity override:
The response is 200 with
POST /api/v2/admin/items/movie-tmdb-43506/split {"file_ids": ["1298283"], "target": {"unmatched": true}, "history_mode": "keep", "persist_override": false, "dry_run": false}"target_content_id": "local-[LOCAL-ID-1]","files_moved": 1,"root_overrides": []and"file_overrides": []. I used the API because of the web app's Split Versions → Detach as unmatched (identify later): by my reading of the code, it sends nopersist_override, so the server would save an override carrying the source item's title (admin_split.go:260,:440-441). I didn't try it in the web app. - Don't start a scan; the split rescans the folder by itself. Wait about 30 s. Here the rescan row came at 12:29:37.004 and the "movie file queue sync" row at 12:29:37.360.
- Look for the log row
metadata: rebound skeleton to existing itemwithfrom_content_idlocal-[LOCAL-ID-1]andto_content_idmovie-tmdb-43506(here at 12:30:04.750). - Check the result:
GET /api/v2/admin/items/movie-tmdb-43506/fileslists files 80798 and 1298283 again (polled right after the rebind row, and again at 12:44:55).GET /api/v2/admin/items/movie-tmdb-125249/filesreturns 404 (polled), and so doesGET /api/v2/catalog/items/movie-tmdb-125249(at 12:44:55).
Expected behavior
The Batman file should become its own movie, movie-tmdb-125249, just as Monk in Europe became its own movie in the same scan. A folder tagged with a TMDB and an IMDb ID should not be merged into a movie whose TMDB and IMDb IDs both differ from the tags.
Where the expectation comes from:
- The user manual, Media folders → Provider IDs: "If a title matches the wrong entry, add its provider ID to the folder name or the file name".
- Wrongly merged "versions" cannot be split, and tagged folders with different provider IDs merge into one item #318, Expected behavior: "Files carrying explicit provider tags never merge across different IDs, and always group across folders with the same ID."
Silo version / commit
1258 · 3f171de (built_at 2026-10-10T02:10:41.000Z)
- On 2026-10-09 the server reported 1201 · ca186fe.
- The retained logs, which begin after 2026-10-09 12:00 UTC, show one restart, at 2026-10-10 12:43 UTC. That was most likely the upgrade (an inference).
- Plugins: silo.tmdb 1.2.26 (auto-updated from 1.2.25 at that restart), silo.mdblist 0.4.0, silo.tvdb 1.4.1 (installed, but not in the provider chains of movie libraries 1, 2, 3 and 6), silo.theintrodb 0.1.3, and the Trakt 0.2.0, MDBList 0.2.0 and Floppy 0.3.1 watch-sync plugins.
Deployment
Docker Compose
Area
Libraries, scanning, and metadata
Clients affected
- Web UI
- Android
- iOS/tvOS/macOS
- Jellyfin-compat client (Infuse, Findroid, ...)
- Other/not client-specific
Client app and device
Not client-specific: the server's catalog is wrong. I saw the older No Way Home case in the web app in Firefox, and in Silo for Android 1.0.0 (20) from Google Play on a Google Pixel 6a, Android 17.
Media and playback details
Not a playback problem. The reproduced file is Batman (1943) [imdb-tt0035665][tmdb-125249][DVD][AC3 2.0][x264].mkv. It's the only video file in its folder (Library roots: observed_file_count 1).
Diagnostics report reference
No response
Relevant logs
# Admin logs API (GET /api/v2/admin/logs/app), rows exactly as returned, one per line.
# The API returns newest first; sections marked "oldest first" were reversed or merged.
# Redactions: [NODE-ID-1] = the server's node ID; [MOVIES-ROOT-1] = the parent folder that holds
# library 1's folders and library 6's folder; local-[LOCAL-ID-1] = the local content ID the split
# created (derived from the file's absolute path; the same value in every row).
# All 69 rows in the reproduction window, and all 307 rows in 2026-10-10 23:03:40-23:04:00
# (around the first Batman merge), are level info. None is a warn row.
#
# The reproduction, 2026-10-11 12:29:30-12:30:15 UTC, oldest first (69 rows)
# [... 64 apiv2 and playback rows omitted ...]
{"id": "27245003", "timestamp": "2026-10-11T12:29:35.275Z", "level": "info", "component": "api", "message": "admin split: item split", "node_id": "[NODE-ID-1]", "attrs": {"actor_user_id": 1, "component": "api", "files_moved": 1, "history_ambiguous": 0, "history_mode": "keep", "history_moved": 0, "progress_moved": 0, "source_content_id": "movie-tmdb-43506", "target_content_id": "local-[LOCAL-ID-1]", "target_created": true}}
{"id": "27245006", "timestamp": "2026-10-11T12:29:37.004Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 1, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/Archive/Batman (1943) [imdb-tt0035665][tmdb-125249]"}}
{"id": "27245007", "timestamp": "2026-10-11T12:29:37.357Z", "level": "info", "component": "scanner", "message": "metadata: series root queue sync", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "folder_id": 1, "scope": "[MOVIES-ROOT-1]/Archive/Batman (1943) [imdb-tt0035665][tmdb-125249]"}}
{"id": "27245008", "timestamp": "2026-10-11T12:29:37.360Z", "level": "info", "component": "scanner", "message": "metadata: movie file queue sync", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "folder_id": 1, "scope": "[MOVIES-ROOT-1]/Archive/Batman (1943) [imdb-tt0035665][tmdb-125249]"}}
{"id": "27245044", "timestamp": "2026-10-11T12:30:04.750Z", "level": "info", "component": "metadata", "message": "metadata: rebound skeleton to existing item", "node_id": "[NODE-ID-1]", "attrs": {"component": "metadata", "from_content_id": "local-[LOCAL-ID-1]", "item_type": "movie", "to_content_id": "movie-tmdb-43506"}}
#
# Every row matching q=rebound skeleton to existing item from before the reproduction, newest first
{"id": "27124366", "timestamp": "2026-10-10T23:20:54.439Z", "level": "info", "component": "metadata", "message": "metadata: rebound skeleton to existing item", "node_id": "[NODE-ID-1]", "attrs": {"component": "metadata", "from_content_id": "movie-tmdb-299287", "item_type": "movie", "to_content_id": "movie-tmdb-43013"}}
{"id": "27122372", "timestamp": "2026-10-10T23:13:14.281Z", "level": "info", "component": "metadata", "message": "metadata: rebound skeleton to existing item", "node_id": "[NODE-ID-1]", "attrs": {"component": "metadata", "from_content_id": "movie-tmdb-595980", "item_type": "movie", "to_content_id": "movie-tmdb-595983"}}
{"id": "27122134", "timestamp": "2026-10-10T23:10:54.549Z", "level": "info", "component": "metadata", "message": "metadata: rebound skeleton to existing item", "node_id": "[NODE-ID-1]", "attrs": {"component": "metadata", "from_content_id": "movie-tmdb-287128", "item_type": "movie", "to_content_id": "movie-tmdb-8422"}}
{"id": "27120234", "timestamp": "2026-10-10T23:04:55.483Z", "level": "info", "component": "metadata", "message": "metadata: rebound skeleton to existing item", "node_id": "[NODE-ID-1]", "attrs": {"component": "metadata", "from_content_id": "movie-tmdb-130717", "item_type": "movie", "to_content_id": "movie-tmdb-65203"}}
{"id": "27119758", "timestamp": "2026-10-10T23:03:52.271Z", "level": "info", "component": "metadata", "message": "metadata: rebound skeleton to existing item", "node_id": "[NODE-ID-1]", "attrs": {"component": "metadata", "from_content_id": "movie-tmdb-125249", "item_type": "movie", "to_content_id": "movie-tmdb-43506"}}
#
# The 2026-10-10 scan: first component=scan queue row between 22:55 and 23:01
# [... 10 later scan queue rows omitted ...]
{"id": "27118070", "timestamp": "2026-10-10T23:00:01.354Z", "level": "info", "component": "scan queue", "message": "scan queue: progress", "node_id": "[NODE-ID-1]", "attrs": {"library_id": 1, "matched_files": 0, "message": "Preparing scan", "phase": "preparing", "processed_files": 0, "retried_items": 0, "scan_id": "01M4M0PVC1JS3BWWQC3J7FRYPB", "total_files": 0}}
# Library-1 "scanner: discovered files" rows, 2026-10-10 22:00-23:30, oldest first
# [... 32 rows for other libraries omitted ...]
{"id": "27118114", "timestamp": "2026-10-10T23:00:10.544Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 6248, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1980"}}
{"id": "27118171", "timestamp": "2026-10-10T23:00:23.247Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 9249, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1990"}}
{"id": "27118560", "timestamp": "2026-10-10T23:00:46.453Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 14071, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2000"}}
{"id": "27118809", "timestamp": "2026-10-10T23:01:20.658Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 22959, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2010"}}
{"id": "27118941", "timestamp": "2026-10-10T23:01:58.628Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 17732, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2020"}}
{"id": "27118985", "timestamp": "2026-10-10T23:02:10.741Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 4124, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/Archive"}}
# The two "scanner: processing progress" rows from 2026-10-10 23:03:40-23:04:00, oldest first
{"id": "27119572", "timestamp": "2026-10-10T23:03:41.249Z", "level": "info", "component": "scanner", "message": "scanner: processing progress", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "errors": 0, "folder_id": 1, "new": 350, "processed": 350, "scope": "[MOVIES-ROOT-1]/Archive", "total": 4124, "unchanged": 0, "updated": 0}}
{"id": "27119743", "timestamp": "2026-10-10T23:03:51.248Z", "level": "info", "component": "scanner", "message": "scanner: processing progress", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "errors": 0, "folder_id": 1, "new": 396, "processed": 396, "scope": "[MOVIES-ROOT-1]/Archive", "total": 4124, "unchanged": 0, "updated": 0}}
#
# When Archive appeared: library-1 "scanner: discovered files" rows from an export of scanner rows
# covering 2026-10-09 13:22 to 2026-10-10 13:24, oldest first. Archive first appears at 13:23:18 with 0 files.
# [... 1,505 rows for other libraries and other scanner messages omitted ...]
{"id": "26793113", "timestamp": "2026-10-09T23:00:01.423Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 6248, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1980"}}
{"id": "26793118", "timestamp": "2026-10-09T23:00:05.632Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 9249, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1990"}}
{"id": "26793130", "timestamp": "2026-10-09T23:00:18.379Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 14071, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2000"}}
{"id": "26793148", "timestamp": "2026-10-09T23:00:28.754Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 22959, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2010"}}
{"id": "26793201", "timestamp": "2026-10-09T23:00:48.444Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 17728, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2020"}}
{"id": "26866991", "timestamp": "2026-10-10T13:16:50.764Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 6248, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1980"}}
{"id": "26867005", "timestamp": "2026-10-10T13:16:56.025Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 9249, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1990"}}
{"id": "26867028", "timestamp": "2026-10-10T13:17:04.642Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 14071, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2000"}}
{"id": "26867062", "timestamp": "2026-10-10T13:17:17.250Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 22959, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2010"}}
{"id": "26867334", "timestamp": "2026-10-10T13:17:49.086Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 17731, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2020"}}
{"id": "26868346", "timestamp": "2026-10-10T13:22:04.848Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 6248, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1980"}}
{"id": "26868359", "timestamp": "2026-10-10T13:22:10.487Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 9249, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/1990"}}
{"id": "26868380", "timestamp": "2026-10-10T13:22:18.295Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 14071, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2000"}}
{"id": "26868417", "timestamp": "2026-10-10T13:22:33.361Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 22959, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2010"}}
{"id": "26868544", "timestamp": "2026-10-10T13:23:08.544Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 17731, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/2020"}}
{"id": "26868571", "timestamp": "2026-10-10T13:23:18.843Z", "level": "info", "component": "scanner", "message": "scanner: discovered files", "node_id": "[NODE-ID-1]", "attrs": {"component": "scanner", "files": 0, "folder_id": 1, "scope": "[MOVIES-ROOT-1]/Archive"}}
#
# Restarts in the retained logs (q=graceful shutdown and q=server listening), merged oldest first
{"id": "26861772", "timestamp": "2026-10-10T12:43:17.471Z", "level": "info", "component": "app", "message": "beginning graceful shutdown", "node_id": "[NODE-ID-1]"}
{"id": "26861799", "timestamp": "2026-10-10T12:43:34.884Z", "level": "info", "component": "app", "message": "HTTP server listening", "node_id": "[NODE-ID-1]", "attrs": {"addr": ":8080"}}
{"id": "26861800", "timestamp": "2026-10-10T12:43:34.885Z", "level": "info", "component": "app", "message": "Jellyfin compat server listening", "node_id": "[NODE-ID-1]", "attrs": {"addr": ":8096"}}
{"id": "26861801", "timestamp": "2026-10-10T12:43:34.885Z", "level": "info", "component": "app", "message": "ABS compat server listening", "node_id": "[NODE-ID-1]", "attrs": {"addr": ":13378"}}Technical notes
Analysis by the AI agent. Line numbers are for main at c32feb1. The functions cited have the same bodies in the running build, 3f171de, but sit at different lines there: service.go gained refresh-debt and episode-linking changes in between. For example, the rebind log line is at L7263 in 3f171de.
Where the merge happens. metadata: rebound skeleton to existing item is logged only in rebindItemByProviderIDsLocked (service.go#L7367-L7396, log at L7390). It has two callers, and the log doesn't say which one ran:
mergeAndPersist, when the item is provisional or has no durable provider IDs (L2431-L2434);- the end of
recoverProviderIDConflict(L7445), which runs after a provider-ID conflict at persist time (L2834, L2896).
Both callers ask findExistingByProviderIDs (L6347-L6382) for an owner. That uses FindContentIDByProviderIDs (provider_id_repo.go#L431), which returns the first item holding any one requested (provider, ID) pair, in tmdb, tvdb, imdb order. If that finds nothing, it falls back to GetByExternalID (item_repo.go#L932), which checks each column on its own. Nothing compares the two items' other IDs, so one shared ID is enough, even when the folder's TMDB and IMDb tags contradict the target.
The new-item dedup in mergeAndPersist (L2380-L2389) uses the same lookup, and it logs nothing. The canonicalize branches of recoverProviderIDConflict (L7431, L7437) are guarded differently. They refuse when both items already have files or library memberships (provider_id_integrity.go:430-431, :508-509).
Which ID was shared: unknown.
- The reproduction may only show a re-merge. In
rebindItemToExistingItem, "dedupe provider ids" (L7516) deletes the source'smedia_item_provider_idsrows only for providers the target already has rows for, and "move provider ids" (L7527) moves the rest onto the target. If Bataan lacked a row for one of Batman's providers, the first merge would have moved that ID to Bataan, and the reproduction would just find it again. I can't see those rows. The query below would show it. - TMDB returns no TVDB ID for 125249, and none for the other nine movies.
- Neither log window has a
warnrow. So no NFO or folder hint conflicted with a stored ID or overrode an earlier hint (L9141, L9147), and no search result conflicting with the trusted IDs was retained (L1530-L1531). - A hint for a key the item doesn't have yet is added silently (L9150). At initial match, NFO
<uniqueid>hints are applied before search (L1488-L1500;docs/architecture/local-nfo-metadata.md:43: "NFO hints beat folder-name hints"). A guess, not checked: a TVDB<uniqueid>in a.nfoin the Batman or Monk folder would produce exactly this. - The Debug rows on this path ("provider search result", "search candidates assembled", L1523 and L1548) log the provider, title, year and counts, not IDs, so raising the log level wouldn't show the shared ID.
- A guess: in every pair the titles share words or letters and the years are at most one apart, so whatever supplied the shared ID may have matched by title.
A read-only query that would show which ID ties each pair together, and when it arrived. A moved row keeps its created_at and gets a new updated_at (L7527):
SELECT content_id, provider, provider_id, created_at, updated_at
FROM media_item_provider_ids
WHERE content_id IN ('movie-tmdb-43506','movie-tmdb-595983','movie-tmdb-8422','movie-tmdb-65203','movie-tmdb-43013')
ORDER BY content_id, provider;Why the July work doesn't cover it. #319, the fix for #318 (0fb5afe), anchored scan group keys on folder tags. #318's acceptance criterion was "Structured provider tags anchor content-group keys (same tag groups, different tags never merge…)". The folders checked (Batman, Monk) do get their own resolved roots with their own IDs, so the merge happens later, in matching. The repair from #459 needs an existing matched owner of the folder's anchored ID (anchored_identity_repair.go#L485-L489). After a merge the tagged movie has no item, so the repair leaves it alone.
Not duplicates. #1954 fixed an item that kept a stale content ID after Identify; here each receiving item's content ID matches its own tmdb_id. #1824 involves a root identity override; the Batman root has "override_source": "none".
Possible direction (a suggestion, not tested). Refuse the rebind and the new-item dedup when the two items disagree on TMDB or IMDb. #2082 added the same rule for people: a name fallback is accepted only when "no provider ID conflicts". The repair could also split out a root whose anchored ID has no owner.
Untested idea for a clean reproduction. The Monk pair has no history before the scan that merged it. Put Monk (1968) [imdb-tt1864420][tmdb-595980] and Monk in Europe (1968) [imdb-tt1864421][tmdb-595983], with whatever sidecar files they have, into one movie library on a fresh instance (provider chain TMDB, MDBList, NFO), then scan. Without knowing where the shared ID came from, this may not reproduce.
AI harness
Claude Code 2.1.294 (in Agent Office)
AI tool(s)
Claude Code with subagents; GitHub CLI, curl, git, and Python scripts against the Silo admin API
AI model(s)
claude-opus-5-5 (all sessions and subagents)
AI involvement
Fully AI-generated, human verified
Independent or adversarial review
Two rounds of fresh Claude Code subagents reviewed the draft against a sanitized evidence file.
-
Round 1 (evidence, reproducibility, privacy, duplicates) found:
- a wrong library for Bataan;
- two log excerpts with unmarked omissions;
- claims wider than the evidence;
- no on-demand reproduction;
- a missing
.nfoprecondition.
The agent then reproduced the bug on the server with my approval and rewrote the draft.
-
Round 2 (evidence, reproducibility, privacy) found:
- a reversed timestamp order;
- an interval taken across two clocks;
- a local ID derived from a file path;
- two more unmarked log filters;
- an overstated code argument about which provider IDs a merge moves;
- older Movies-4K cases with a different shape.
All were fixed. The older cases moved to a follow-up comment.
The duplicate search found no matching open or closed issue or PR. Still open: the query above and the folders' .nfo contents.
Confirmations
- I searched open and recently closed issues and pull requests, and this is not already reported
- I reproduced this myself on a real deployment
- Any logs provided are raw apart from marked redactions and are not AI-summarized; if none were available, I said so above
Activity
silo-triage commentedon Oct 11, 2026
Do the four folders in the Batman/Bataan and Monk/Monk in Europe pairs contain
.nfofiles? If so, please share only their provider-ID fields (<uniqueid>and any legacy ID fields), with private details removed. This would establish a missing precondition for a clean reproduction without repeating the split on your live library.amokster88 commentedon Oct 11, 2026
None of the four folders contains a
.nfofile, so there are no<uniqueid>or legacy ID fields to share:Batman (1943) [imdb-tt0035665][tmdb-125249]Bataan (1943) [imdb-tt0035664][tmdb-43506]Monk (1968) [imdb-tt1864420][tmdb-595980]Monk in Europe (1968) [imdb-tt1864421][tmdb-595983]The NFO provider is enabled in these libraries' chains, but it had nothing to read for these titles. So the shared ID didn't come from an NFO identity hint, and the guess under "Which ID was shared" in the report doesn't apply.
I can't run the
media_item_provider_idsquery from the report. I have admin access to the server, but no shell or database access, and I found no API that returns those rows. The catalog, Jellyfin-compatible and download-manifest responses only carry the item'stmdb_id,imdb_idandtvdb_idcolumns.AI disclosure: Claude Code 2.1.294, claude-opus-5-5, AI-assisted (I checked the folders; the agent wrote this comment and checked the API contract). Review: n/a.