Skip to content

fix(downloads): SABnzbd category filter hid tracked jobs reassigned to Default, stranding them unimported#759

Open
t3chnaztea wants to merge 1 commit into
Listenarrs:canaryfrom
t3chnaztea:fix/sabnzbd-category-mismatch-strands-downloads
Open

fix(downloads): SABnzbd category filter hid tracked jobs reassigned to Default, stranding them unimported#759
t3chnaztea wants to merge 1 commit into
Listenarrs:canaryfrom
t3chnaztea:fix/sabnzbd-category-mismatch-strands-downloads

Conversation

@t3chnaztea

Copy link
Copy Markdown

The bug

SABnzbd silently reassigns jobs with unknown categories to Default (*). Listenarr sends cat=<configured> on add, but every read mapper filtered slots by configured category before matching nzo_id (SabnzbdResponseMapper.MapQueueSlotToQueueItem / MapHistorySlotToQueueItem). So when the configured category doesn't exist in SABnzbd, a job Listenarr itself grabbed becomes invisible to it: the monitor reconciles by nzo_id but never sees the slot, the download shows "1 tracked but 0 in client", and it never progresses or imports. Notably the history endpoint is already fetched unfiltered — the recovery data was in hand and discarded client-side.

The fix

Two parts, mirroring how Radarr treats categories (reconcile items you grabbed by download ID; category scopes discovery, not tracked items):

  1. Tracked nzo_ids bypass the category filter. The fetch workflow already builds the monitored-id set; it now flows into both QueueItem mappers as an optional parameter, and a slot whose nzo_id is tracked is returned regardless of its category. Untracked discovery/display filtering is unchanged. (MapQueueSlotToDownloadClientItem is deliberately untouched: the item-fetch path has no monitored set and isn't the strand path.)
  2. Connection test warns about missing categories. After the version check passes, the tester probes mode=get_cats and, if the configured category is absent, returns an advisory ("category 'x' does not exist in SABnzbd. Jobs will fall into Default and may not import. Create the category…") — still a passing test, and the probe is best-effort (any get_cats failure degrades to a clean pass).

Tests

  • QueueSlot_InDifferentCategory_ButTracked_IsReturned / HistorySlot_InDifferentCategory_ButTracked_IsReturned
  • QueueSlot_InDifferentCategory_NotTracked_IsFiltered / HistorySlot_InDifferentCategory_NotTracked_IsFiltered (discovery filtering preserved)
  • TestConnectionAsync_CategoryMissingFromSabnzbd_ReturnsAdvisoryPass / TestConnectionAsync_CategoryPresentInSabnzbd_ReturnsCleanPass
  • One existing test updated: Sabnzbd_GetQueueAndItems_FilterByConfiguredCategory passed both nzo_ids as tracked yet asserted the foreign-category one was hidden — it encoded exactly this bug. Its intent (respect configured category) is a discovery concern, so it now exercises the no-tracked-ids path and keeps its assertions.

Full suite green: 1195 passed / 0 failed; dotnet format --verify-no-changes clean.

🤖 Generated with Claude Code

…o Default, stranding them unimported; warn when category missing

SABnzbd silently reassigns jobs with an unknown category to Default (*), but
every SABnzbd read mapper filtered slots by the configured category before
matching nzo_id, so a job Listenarr itself grabbed became invisible ("tracked
but 0 in client") and never imported. The queue/history mappers now bypass the
category filter for any slot whose nzo_id is in the monitored set, reconciling
grabbed items by download ID while category filtering still scopes untracked
discovery. The connection tester also probes mode=get_cats and returns an
advisory (not a hard failure) when the configured category is absent from
SABnzbd, so misconfiguration surfaces before jobs strand.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@t3chnaztea
t3chnaztea requested a review from a team July 13, 2026 02:56
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.

1 participant