Document data-loss incident, pause qbt-relink.sh

Babylon 5 S03/S04 (44 episodes) were destroyed during a relink
attempt despite every safety check the script performs (HTTP status,
save_path, content_path) passing. qBittorrent's own automatic
incomplete-file management took further unrequested action after
the script's calls succeeded - moving/truncating files at a
location it still considered incomplete - which none of the
script's checks could see coming since they only verify immediately
after its own calls.

No backup existed. Files could not be found anywhere on /data after
a full search. Marking qbt-relink.sh unsafe until the interaction
with qBittorrent's temp_path_enabled behavior is understood well
enough to prevent this.

Claude-Session: https://claude.ai/code/session_01HZQK6jHmdTpFjFZM8FUnqA
This commit is contained in:
2026-09-08 10:13:07 +00:00
parent 027108b492
commit 49e47a32bf
+60 -1
View File
@@ -5,6 +5,20 @@ description: Use Sonarr to add TV shows and manually import existing raw-named f
# Sonarr library migration/import # Sonarr library migration/import
**⚠️ `qbt-relink.sh` is currently UNSAFE — do not use it until this warning is
removed.** It caused real, apparently unrecoverable data loss (Babylon 5
seasons 3 and 4, 44 episodes) on 2026-09-08: qBittorrent's own automatic
incomplete-file management fought the script's manual `setLocation` calls
across repeated resume/stop cycles and physically moved/destroyed the real
files, even after every safety check the script performs (stopped state,
confirmed `save_path`, confirmed `content_path`) reported success. See
"Incident 3" below for the full detail. **The Sonarr import workflow itself
(`sonarr.sh scan`/`import`) is unaffected and still safe** — it's specifically
the qBittorrent-side relink step that's paused. Until this is fixed, leave
qBittorrent's stale torrent entries alone after a Sonarr import rather than
touching them — a `missingFiles` error in the qBittorrent UI is a much
smaller problem than what happened here.
This repo's Sonarr manages TV show organization for both Jellyfin and Plex This repo's Sonarr manages TV show organization for both Jellyfin and Plex
(they share the same `nas_media` library at `/data/video/tv`). The (they share the same `nas_media` library at `/data/video/tv`). The
`claude-homelab` plugin's Sonarr skill (if installed) only covers `claude-homelab` plugin's Sonarr skill (if installed) only covers
@@ -130,7 +144,52 @@ or its `state` is `checkingDL` instead of `checkingUP`**, check
`content_path` vs `save_path` directly — a mismatch means the recheck is `content_path` vs `save_path` directly — a mismatch means the recheck is
running against the wrong location and will "succeed" at finding nothing. running against the wrong location and will "succeed" at finding nothing.
### Incident: an unchecked `setLocation` call let a torrent start re-downloading ### Incident 3: qBittorrent's own automatic file management destroyed real data despite every check passing — data loss, `qbt-relink.sh` is now paused
2026-09-08, same overall session, later batch. Even with incidents 1 and 2's
fixes in place (`api_call` status checking, torrent stopped first,
`save_path` *and* `content_path` both confirmed correct before proceeding),
Babylon 5 S03 and S04 lost their actual files. The qBittorrent log
(`/api/v2/log/main`) tells the story: after `setLocation` moved them to
`Season 03`/`Season 04` (logged success), qBittorrent's *own* internal logic
immediately enqueued and executed a further move of those same files **back**
to `/data/torrents/incomplete/video/tv` on its own — nothing in the script
requested this. A later resume cycle moved them back to `Season 03`/`04`
again, but then repeated automatic resume/stop cycles followed (visible as
alternating `"Torrent resumed"`/`"Torrent stopped"` log lines with no
corresponding script action), and after those, both `Season 03` and
`Season 04` folders were **completely empty** — confirmed via `ls`/`stat`
from two separate containers, and a full search of `/data` found the files
nowhere. 44 episodes, gone. No backup existed (the user had explicitly
decided to proceed without one earlier in this project).
Best-guess mechanism: qBittorrent's `temp_path_enabled` incomplete-file
management re-evaluates on every resume, independent of manual
`setLocation`/`setDownloadPath` calls, and can decide to relocate — or,
worse, truncate/overwrite in place expecting fresh downloaded data — files
at a location it still considers "incomplete" (i.e. never successfully
verified as 100% via a *clean* recheck). Since these torrents' recheck kept
getting interrupted/retriggered across multiple attempts (network/NFS
slowness caused several manual re-triggers in this session), the torrent
seems to have never reached a stable "verified complete" state internally,
leaving it perpetually eligible for this automatic (and here, destructive)
relocation — regardless of what the script's own state checks reported.
**This means the safety checks this script relies on (`save_path`,
`content_path`, HTTP status) are necessary but not sufficient** — they
confirm the *script's* requests succeeded, but qBittorrent can still take
further unrequested action afterward that undoes or destroys the result,
and none of that is visible to a script that only checks immediately after
its own calls. `qbt-relink.sh` is paused (see the warning at the top of this
file) until this is understood well enough to prevent it — likely candidates
for a real fix: disabling `temp_path_enabled` globally before relinking
(and confirming no other torrent depends on it) as a global preference, an
approach that fully separates the qBittorrent-tracked download from the
Sonarr-organized library so they never share a path, or verifying via a
polling loop that a recheck reaches a genuinely stable end state (not just
"currently reports 100% right now") before considering a torrent safe.
### Incident 1: an unchecked `setLocation` call let a torrent start re-downloading
The first version of `qbt-relink.sh` fired `setLocation`/`renameFile`/ The first version of `qbt-relink.sh` fired `setLocation`/`renameFile`/
`recheck` with `curl -s ... > /dev/null`, discarding the HTTP status. Running `recheck` with `curl -s ... > /dev/null`, discarding the HTTP status. Running