Skip to content

fix(sabnzbd): mark history slot Failed when reported path is missing#605

Open
firestaerter3 wants to merge 2 commits into
javi11:mainfrom
firestaerter3:fix/sab-history-failed-on-missing-path
Open

fix(sabnzbd): mark history slot Failed when reported path is missing#605
firestaerter3 wants to merge 2 commits into
javi11:mainfrom
firestaerter3:fix/sab-history-failed-on-missing-path

Conversation

@firestaerter3
Copy link
Copy Markdown
Contributor

Summary

If altmount's calculateHistoryStoragePath finds that the reported symlink path doesn't exist on disk, the SAB-history response still ships the slot with status=Completed. Sonarr/Radarr then queue the item and hit FileNotFoundException on import.

This is the loop described in #596. The fix marks the slot as Failed when the path is missing, so the ARR removes it cleanly through its normal failed-download path.

What changed

  • internal/api/sabnzbd_handlers.go: calculateHistoryStoragePath now returns (path string, exists bool). The two callers (respondSABnzbdHistory, respondSABnzbdHistoryByIDs) override the slot's Status to Failed and set a FailMessage when !exists.
  • internal/api/sabnzbd_types.go: small markHistorySlotMissing helper to overwrite an existing slot's status.
  • internal/api/sabnzbd_types_test.go: unit tests for the missing-path override, including pre-existing fail_message preservation and nil safety.

What this does NOT do

It does not retry symlink creation. If Import.ImportDir is writable, new imports create symlinks normally; this only ensures historical items where creation already failed get reported to the ARR cleanly instead of looping forever.

Test plan

  • Tests added in sabnzbd_types_test.go.
  • I couldn't run go test locally (Go 1.26.0 toolchain plus a DNS issue on my end fetching modules). Relying on CI here. Happy to iterate if anything fails.

Fixes #596.

calculateHistoryStoragePath already detects when the path it's about to
report doesn't exist on disk, but only logs a WARN and ships the slot to
Sonarr/Radarr with status=Completed anyway. The ARRs then queue the
item and loop on FileNotFoundException at import time forever.

This change returns an exists bool alongside the path. Both callers
(respondSABnzbdHistory, respondSABnzbdHistoryByIDs) overwrite the slot
to Failed via a new markHistorySlotMissing helper, so the ARR removes
the item cleanly through its normal failed-download path.

Does not retry symlink creation; that's a follow-up. This is the
minimal fix to stop the loop for items whose symlink under
Import.ImportDir was never created at import time.

Refs: javi11#596
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ffe4e22ffe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/api/sabnzbd_handlers.go Outdated
Comment on lines +1018 to +1019
if !exists {
markHistorySlotMissing(&slot, finalPath)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid marking old completed history rows as failed

This now rewrites any history slot to Failed whenever os.Stat on the reported path returns not-exist, but respondSABnzbdHistoryByIDs also serves rows reconstructed from persistent import history (importHistoryToQueueItem), which represent past successful imports rather than current filesystem state. In this repo, files can be legitimately removed later (for example via ARR delete/upgrade handling in internal/api/arrs_handlers.go), so a previously successful item can be reclassified as failed long after completion, causing Sonarr/Radarr to run failed-download handling on stale IDs instead of treating them as historical completions.

Useful? React with 👍 / 👎.

…path

Persistent ImportHistory rows that respondSABnzbdHistory and
respondSABnzbdHistoryByIDs reconstruct via importHistoryToQueueItem
represent past successful imports. Their on-disk file may have been
legitimately deleted later by ARR upgrade/cleanup handlers, so
calculateHistoryStoragePath will report exists=false for them on a
fresh poll.

Without gating, the previous commit would reclassify those historical
successes as Failed, causing Sonarr/Radarr to run failed-download
handling on stale completed imports.

Track item provenance via a pointer-keyed set populated only when the
item came from the live queue with status=Completed. Apply
markHistorySlotMissing only for those, leaving historical rows
untouched.

Refs: javi11#596
@firestaerter3
Copy link
Copy Markdown
Contributor Author

Good catch, thanks. Fixed in 1932a1e.

The fix now gates markHistorySlotMissing on a per-item set (liveCompleted) populated only when the item came from the live queue with Status=Completed. Rows reconstructed from persistent ImportHistory via importHistoryToQueueItem are left untouched, so a past success whose file was later removed by ARR cleanup keeps its Completed status.

Applied to both respondSABnzbdHistory and respondSABnzbdHistoryByIDs.

@javi11
Copy link
Copy Markdown
Owner

javi11 commented May 22, 2026

Dou you tested this on your setup and is working as expected? as first glance seems fine

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SAB-history reports paths that do not exist on disk → ARR queues fill with FileNotFoundException

2 participants