Downloads in SABnzbd history waiting for import in Sonarr queue incorrectly flagged as unknown client #74

Closed
opened 2026-05-29 14:12:15 +01:00 by Gandalf · 4 comments
Owner

Summary

Downloads that are active/completed but waiting for manual import/interaction in the Sonarr (or Radarr) queue, and have finished downloading in SABnzbd (residing in SABnzbd history), are incorrectly categorized as "unknown" client / "Orphaned (unconfigured client)" downloads.

Technical Analysis & Root Cause

The root cause lies in the title-matching fallback branch of matchSabHistory in server/services/DownloadMatcher.js:

  1. matchSabHistory is called by DownloadBuilder.js to match SABnzbd history slots against *arr queue and history records.
  2. For each history slot, it tries to match by downloadId (matchesSabId) against:
    • sonarrHistoryRecords / radarrHistoryRecords
    • sonarrQueueRecords / radarrQueueRecords (added as a dual-lookup safeguard).
  3. If the direct ID lookup fails (or is absent), it falls back to a title-matching check using titleMatches:
    // Fallback: Check by title matching
    if (!sonarrMatch) {
      sonarrMatch = sonarrHistoryRecords.find(r => {
        const rTitle = r.title || r.sourceTitle;
        return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' });
      });
    }
  1. However, the title-matching fallback block ONLY checks against sonarrHistoryRecords / radarrHistoryRecords. It completely ignores sonarrQueueRecords and radarrQueueRecords.
  2. Because the download is still waiting for import, it remains in the active *arr queue (not the history yet).
  3. Consequently, the title-matching fallback fails to associate the completed SABnzbd history slot with the active *arr queue record.
  4. During the secondary orphans pass in DownloadBuilder.js, matchOrphanedArrRecords discovers the unmatched queue record, fails to find a matched download client item, and compiles it as a standalone synthetic card labelled as Orphaned (unconfigured client) / "unknown" client.

Impact

  • Aesthetic & Diagnostic Confusion: Completed downloads awaiting import incorrectly display as dimmed, dashed cards from an "unknown client", rather than standard completed cards from SABnzbd.
  • Loss of Download Client Context: Users lose visibility into the fact that the download client responsible for the download was SABnzbd, which can hinder debugging.

Proposed Solution

Update matchSabHistory in server/services/DownloadMatcher.js to perform the title-matching fallback search against the active QueueRecords when HistoryRecords yield no matches, mirroring the dual-lookup strategy:

    // Fallback: Check by title matching
    if (!sonarrMatch) {
      sonarrMatch = sonarrHistoryRecords.find(r => {
        const rTitle = r.title || r.sourceTitle;
        return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' });
      });
    }
    if (!sonarrMatch) {
      sonarrMatch = sonarrQueueRecords.find(r => {
        const rTitle = r.title || r.sourceTitle;
        return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' });
      });
    }

And do the same for radarrMatch and radarrQueueRecords.

### Summary Downloads that are active/completed but waiting for manual import/interaction in the Sonarr (or Radarr) queue, and have finished downloading in SABnzbd (residing in SABnzbd history), are incorrectly categorized as `"unknown"` client / `"Orphaned (unconfigured client)"` downloads. ### Technical Analysis & Root Cause The root cause lies in the title-matching fallback branch of `matchSabHistory` in `server/services/DownloadMatcher.js`: 1. `matchSabHistory` is called by `DownloadBuilder.js` to match SABnzbd history slots against *arr queue and history records. 2. For each history slot, it tries to match by `downloadId` (`matchesSabId`) against: * `sonarrHistoryRecords` / `radarrHistoryRecords` * `sonarrQueueRecords` / `radarrQueueRecords` (added as a dual-lookup safeguard). 3. If the direct ID lookup fails (or is absent), it falls back to a title-matching check using `titleMatches`: ```javascript // Fallback: Check by title matching if (!sonarrMatch) { sonarrMatch = sonarrHistoryRecords.find(r => { const rTitle = r.title || r.sourceTitle; return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' }); }); } ``` 4. However, the title-matching fallback block ONLY checks against `sonarrHistoryRecords` / `radarrHistoryRecords`. It completely ignores `sonarrQueueRecords` and `radarrQueueRecords`. 5. Because the download is still waiting for import, it remains in the active *arr **queue** (not the history yet). 6. Consequently, the title-matching fallback fails to associate the completed SABnzbd history slot with the active *arr queue record. 7. During the secondary orphans pass in `DownloadBuilder.js`, `matchOrphanedArrRecords` discovers the unmatched queue record, fails to find a matched download client item, and compiles it as a standalone synthetic card labelled as `Orphaned (unconfigured client)` / `"unknown"` client. ### Impact * **Aesthetic & Diagnostic Confusion:** Completed downloads awaiting import incorrectly display as dimmed, dashed cards from an "unknown client", rather than standard completed cards from SABnzbd. * **Loss of Download Client Context:** Users lose visibility into the fact that the download client responsible for the download was SABnzbd, which can hinder debugging. ### Proposed Solution Update `matchSabHistory` in `server/services/DownloadMatcher.js` to perform the title-matching fallback search against the active `QueueRecords` when `HistoryRecords` yield no matches, mirroring the dual-lookup strategy: ```javascript // Fallback: Check by title matching if (!sonarrMatch) { sonarrMatch = sonarrHistoryRecords.find(r => { const rTitle = r.title || r.sourceTitle; return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' }); }); } if (!sonarrMatch) { sonarrMatch = sonarrQueueRecords.find(r => { const rTitle = r.title || r.sourceTitle; return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' }); }); } ``` And do the same for `radarrMatch` and `radarrQueueRecords`.
Gandalf added the Kind/Bug
Priority
Medium
3
Area/Matching
labels 2026-05-29 14:12:15 +01:00
Author
Owner

Evaluation of Issue #74 against the develop branch codebase (as of 2026-05-29)

Issue Summary (confirmed):

  • Title: Downloads in SABnzbd history waiting for import in Sonarr queue incorrectly flagged as unknown client
  • Labels: Area/Download Clients, Area/History, Area/Matching, Kind/Bug
  • Status: Open (reported today by Gandalf)
  • Core Problem: Completed SABnzbd downloads (in SAB history) that are still sitting in the Sonarr/Radarr queue (awaiting manual import or other interaction) are being incorrectly treated as "unknown" / "Orphaned (unconfigured client)" instead of being properly attributed to the SABnzbd client.

The technical analysis is 100% precise and accurate. The root cause is exactly as described.


Codebase Confirmation (develop branch)

1. matchSabHistory (the function explicitly called out)

When the downloadId match fails (common in the “still in *arr queue” scenario), the title fallback never looks at sonarrQueueRecords / radarrQueueRecords. It only checks history:

// Fallback: title matching — **ONLY history records**
if (!sonarrMatch) {
  sonarrMatch = sonarrHistoryRecords.find(r => {
    const rTitle = r.title || r.sourceTitle;
    return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' });
  });
}

2. Contrast with matchSabSlots (active SAB queue)

The sibling function matchSabSlots (for items still downloading in SAB) does it correctly:

  • ID lookup: queue → history
  • Title fallback: queue first, then history

This asymmetry is the direct cause of the reported symptom.

3. Downstream effect in DownloadBuilder.js

Because the SAB history item never matched the still-queued *arr record, the *arr queue item falls through to the orphan path and gets the "unknown client" treatment.


Assessment

Aspect Evaluation Severity
Root cause accuracy 100% correct High
Impact High — breaks visibility of real client source for a very common workflow (manual import, post-processing, etc.) High
Regression risk Low — the proposed change mirrors existing logic in matchSabSlots Low
Test coverage No dedicated unit tests visible for this edge case in the inspected files Medium

Recommended Fix

Apply the exact pattern proposed in the issue, but make it symmetric with how matchSabSlots already does the second fallback:

if (!sonarrMatch) {
  sonarrMatch = sonarrHistoryRecords.find(...); // existing
}
if (!sonarrMatch) {
  sonarrMatch = sonarrQueueRecords.find(r => {
    const rTitle = r.title || r.sourceTitle;
    return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' });
  });
}

This brings matchSabHistory into parity with matchSabSlots.


Additional Observations / Suggestions

  1. Refactoring: The ID + title fallback logic has minor duplication between matchSabHistory and matchSabSlots. Consider a small refactoring helper to keep things unified in the long run.
  2. Logging: The existing titleMatches(..., { isFallback: true }) debug logs will automatically surface these new matches, which is helpful.
  3. Edge cases to verify:
    • SAB history item whose nzo_id does match the *arr queue downloadId (should already work via dual-lookup).
    • Items that are in both SAB history and *arr history (normal completed case).
    • Title-only matches with special characters / scene naming.

Verdict: This is a legitimate, well-documented bug. The analysis is spot-on, the code confirms it, and the proposed fix is minimal, safe, and correct. Implementing this fix on develop and verifying it via unit/integration tests will resolve this issue.

### Evaluation of Issue #74 against the develop branch codebase (as of 2026-05-29) **Issue Summary (confirmed):** * **Title:** Downloads in SABnzbd history waiting for import in Sonarr queue incorrectly flagged as unknown client * **Labels:** Area/Download Clients, Area/History, Area/Matching, Kind/Bug * **Status:** Open (reported today by Gandalf) * **Core Problem:** Completed SABnzbd downloads (in SAB history) that are still sitting in the Sonarr/Radarr queue (awaiting manual import or other interaction) are being incorrectly treated as "unknown" / "Orphaned (unconfigured client)" instead of being properly attributed to the SABnzbd client. The technical analysis is 100% precise and accurate. The root cause is exactly as described. --- ### Codebase Confirmation (develop branch) #### 1. `matchSabHistory` (the function explicitly called out) When the `downloadId` match fails (common in the “still in *arr queue” scenario), the title fallback never looks at `sonarrQueueRecords` / `radarrQueueRecords`. It only checks history: ```javascript // Fallback: title matching — **ONLY history records** if (!sonarrMatch) { sonarrMatch = sonarrHistoryRecords.find(r => { const rTitle = r.title || r.sourceTitle; return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' }); }); } ``` #### 2. Contrast with `matchSabSlots` (active SAB queue) The sibling function `matchSabSlots` (for items still downloading in SAB) does it correctly: * **ID lookup:** queue → history * **Title fallback:** queue first, then history This asymmetry is the direct cause of the reported symptom. #### 3. Downstream effect in `DownloadBuilder.js` Because the SAB history item never matched the still-queued *arr record, the *arr queue item falls through to the orphan path and gets the "unknown client" treatment. --- ### Assessment | Aspect | Evaluation | Severity | | :--- | :--- | :--- | | **Root cause accuracy** | 100% correct | High | | **Impact** | High — breaks visibility of real client source for a very common workflow (manual import, post-processing, etc.) | High | | **Regression risk** | Low — the proposed change mirrors existing logic in `matchSabSlots` | Low | | **Test coverage** | No dedicated unit tests visible for this edge case in the inspected files | Medium | --- ### Recommended Fix Apply the exact pattern proposed in the issue, but make it symmetric with how `matchSabSlots` already does the second fallback: ```javascript if (!sonarrMatch) { sonarrMatch = sonarrHistoryRecords.find(...); // existing } if (!sonarrMatch) { sonarrMatch = sonarrQueueRecords.find(r => { const rTitle = r.title || r.sourceTitle; return rTitle && titleMatches(nzbName, rTitle, { isFallback: true, caller: 'matchSabHistory' }); }); } ``` This brings `matchSabHistory` into parity with `matchSabSlots`. --- ### Additional Observations / Suggestions 1. **Refactoring:** The ID + title fallback logic has minor duplication between `matchSabHistory` and `matchSabSlots`. Consider a small refactoring helper to keep things unified in the long run. 2. **Logging:** The existing `titleMatches(..., { isFallback: true })` debug logs will automatically surface these new matches, which is helpful. 3. **Edge cases to verify:** * SAB history item whose `nzo_id` does match the *arr queue `downloadId` (should already work via dual-lookup). * Items that are in both SAB history and *arr history (normal completed case). * Title-only matches with special characters / scene naming. **Verdict:** This is a legitimate, well-documented bug. The analysis is spot-on, the code confirms it, and the proposed fix is minimal, safe, and correct. Implementing this fix on `develop` and verifying it via unit/integration tests will resolve this issue.
Gandalf added the Area/Download ClientsArea/History labels 2026-05-29 14:28:45 +01:00
Author
Owner

Resolved in commit 87387aaebe.

Resolved in commit 87387aaebe4f0485bc57ca1b0cab1bb753196ae2.
Author
Owner

The downloads that are in the SAB history are still shown as "Unknown"

The downloads that are in the SAB history are still shown as "Unknown"
Gandalf reopened this issue 2026-05-29 14:50:21 +01:00
Author
Owner

Resolved in commit b2a837b173.

Resolved in commit b2a837b17352c1bac079a367214325a662a95874.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Gandalf/sofarr#74