Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[BUG] Infinite pending-import retry recreates user-deleted authors as monitored and ignores ImportListExclusions

Open
#264 1 comment 1 reaction 0 assignees View on GitHub

Maintainers usually reply within 7 days

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
45/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
csharp, docker, postgresql
Domain
backend, databases

Research direction

Start with src/NzbDrone.Core/Books/Commands/ProcessPendingImportsCommandHandler.cs, then read AuthorLibraryService.cs, PendingAuthorImportService.cs, and PendingAuthorImportRepository.cs at the paths named in the issue. Trace how exclusions, deleted authors, and unresolved requested works are handled, and compare exclusion matching with ImportListSyncService. Done means retries cannot resurrect excluded authors, removed lists do not leave orphaned retries, and work that cannot resolve can reach a terminal state.

Written by the indexing model from the issue text.

Description

bug

Describe the bug

A PendingAuthorImport row whose requested works can never be resolved retries forever (MaxAttempts = 0, no terminal condition). Each retry calls AuthorLibraryService.AddAuthorAsync with the monitoring config stored on the row. If the user has since deleted the author, the retry re-inserts it monitored (both media sides) with MonitorNewItems = All.

Two things make this unrecoverable from the UI:

  1. ImportListExclusions is never consulted on the pending-queue path, so the exclusion that the UI creates on "delete author → add import list exclusion" does not stop the retry.
  2. Removing the import list that queued the row does not purge or cancel its pending rows.

Observed in production: one row at 415 attempts over ~2 days, reinstating the author roughly every 5 minutes.

To Reproduce

  1. Have a Hardcover import list whose request includes a work that the metadata server keeps out of the author's catalog (announced/upcoming work, or a work with no editions). The row never reaches the success path and is retried indefinitely with errors like:

    Requested Ebook work hc:XXXXXX, Ebook work hc:YYYYYY is not yet present in the authoritative author catalog.
    Requested work(s) hc:ZZZZZZ ... are still being prepared by the metadata server.
    
  2. Let it retry. AttemptCount climbs without bound; OverallStatus stays Retrying (3); MaxAttempts stays 0.

  3. Delete the imported author from the library, with "add import list exclusion" checked.

  4. Observe: within ~5 minutes the author is re-added and monitored. The exclusion does not prevent it.

Expected behavior

  • Retries should not resurrect an author the user deliberately deleted.
  • An ImportListExclusion for the row's provider ID should stop, cancel, or refuse the retry.
  • Removing an import list should not leave permanently-retrying orphan rows behind.
  • A row that cannot ever succeed should reach a terminal state instead of retrying forever.

Root cause (source, running develop @ e67374f)

src/NzbDrone.Core/Books/Commands/ProcessPendingImportsCommandHandler.cs

  • ProcessPendingImportAsync (line 198) calls AddAuthorAsync(item.ProviderId, config) (line 226) before any check on whether the requested works can be resolved.
  • When they can't be, it calls _pendingImportService.ScheduleRetry(item, reason) (lines 254/270) and returns — the success path (MarkSucceeded line 280 + TryDeleteIfUnchanged) is unreachable, so the row is immortal.
  • Nothing in this handler reads ImportListExclusions.

src/NzbDrone.Core/Books/Services/AuthorLibraryService.cs

  • Line 331: ApplyMonitoringConfig(author, config) sets the media gates from the row's stored config before any existence check.
  • Line 351: if no author exists locally, the create path runs — line 389 _authorService.AddAuthor(author, false) inserts the recreated author with those gates applied. This is the resurrection.
  • Line 354: if the author does exist, HandleExistingAuthorAsync → line 1068 ApplyExistingAuthorProgressiveSettings, which routes into UpdateAuthorProgressiveSettings. That method does NULL-only fills (if (!author.AudiobookMonitored.HasValue && audiobookMonitored.HasValue)), so an author that was merely unmonitored keeps its explicit false — the damage is specifically on the delete-then-recreate path.

src/NzbDrone.Core/Books/Services/PendingAuthorImportService.cs

  • Cancel(int id) (line 677) exists and sets a terminal status, but nothing calls it automatically when an exclusion is added or a list is removed.

src/NzbDrone.Core/Books/Repositories/PendingAuthorImportRepository.cs

  • GetDueForProcessing (line 45) selects rows with OverallStatus == Pending || Retrying and NextAttemptAt <= cutoff — no attempt ceiling.

Evidence

Pending rows at time of filing (5 rows, all MaxAttempts = 0):

Id   ProviderId     OverallStatus  AttemptCount  LastError
103  hc:NNNNNN      3 (Retrying)   415           Requested Ebook work hc:XXXXXX, hc:YYYYYY is not yet present in the authoritative author catalog.
122  hc:NNNNNN      3 (Retrying)   96            Requested Ebook work hc:XXXXXX, hc:YYYYYY is not yet present in the authoritative author catalog.
117  hc:NNNNNN      3 (Retrying)   93            Requested Audiobook work hc:XXXXXX, hc:YYYYYY, Ebook work hc:XXXXXX is not yet present in the authoritative author catalog.
118  hc:NNNNNN      3 (Retrying)   95            Requested Ebook work hc:XXXXXX is not yet present in the authoritative author catalog.
119  hc:NNNNNN      3 (Retrying)   97            Requested work(s) hc:XXXXXX, hc:XXXXXX are still being prepared by the metadata server.

(provider/work IDs redacted; happy to supply on request)

Log, repeating every ~5 minutes indefinitely:

2026-09-29 01:17:53.5|Info|ProcessPendingImportsCommandHandler|Processing pending import 103 for provider hc:NNNNNN
2026-09-29 01:17:53.5|Info|ProcessPendingImportsCommandHandler|Processing queued author import for provider hc:NNNNNN
2026-09-29 01:17:54.8|Info|AuthorLibraryService|Author 'Author A' already exists in database with 45 books (audiobook=True, ebook=True)
...
2026-09-29 09:18:16.1|Info|AuthorLibraryService|Author 'Author A' already exists in database with 45 books (audiobook=True, ebook=True)

ImportListExclusions contains the correct exclusion for that provider ID, created via the delete-author flow. ImportLists is empty (list removed) — the pending rows survived.

Suggested fixes

  1. Honour ImportListExclusions in ProcessPendingImportsCommandHandler (same MatchesExclusionId semantics as ImportListSyncService) — skip and cancel the row.
  2. Cancel pending rows for an author when the user deletes it with "add import list exclusion", and/or when the queueing import list is removed.
  3. Do not re-apply AudiobookMonitored/EbookMonitored/MonitorNewItems from a stored row when the author already exists and the user has explicitly changed those settings — the manual-override flags (AudiobookSettingsManuallyOverridden / EbookSettingsManuallyOverridden) exist for exactly this and are not consulted here.
  4. Give the "requested work not present in catalog" branch a terminal or long-backoff state (it can never resolve on its own). Related: #119 covers the unbounded-retry family.

Workaround

The supported cancel endpoint stops it, and the row then leaves the retry set (GetDueForProcessing only selects Pending/Retrying):

curl -X DELETE -H "X-Api-Key: $KEY" http://HOST:8789/api/v1/pendingauthorimport/103
# -> {"message": "Pending import cancelled"}

Finding the stuck rows requires GET /api/v1/pendingauthorimport. The API was the only path I found to list or cancel these rows once the author is absent from the library, which is why this is easy to hit and hard to diagnose.

System Information:

  • OS: Ubuntu 24.04 (Docker container, Unraid host)
  • Chaptarr Version: 0.9.965.0 (develop), migrationVersion 107
  • Installation Method: Docker (chaptarr/chaptarr:develop)
  • Database: PostgreSQL 18 (not SQLite — this is not the #119 lock cascade)
Dominant language
C#
Stars
664
Forks
48
Avg merge
3d 5h
Merged PRs (30d)
1

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Chaptarr/chaptarr

All issues in Chaptarr/chaptarr

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.