Skip to content

fix(requests): reset orphaned season statuses when a request is deleted#3279

Open
Knat-Dev wants to merge 1 commit into
seerr-team:developfrom
Knat-Dev:fix/reset-season-statuses-on-request-delete
Open

fix(requests): reset orphaned season statuses when a request is deleted#3279
Knat-Dev wants to merge 1 commit into
seerr-team:developfrom
Knat-Dev:fix/reset-season-statuses-on-request-delete

Conversation

@Knat-Dev

@Knat-Dev Knat-Dev commented Jul 21, 2026

Copy link
Copy Markdown

Description

Once a user deletes a request the season rows of the request stay PENDING/PROCESSING forever, so the show's seasons can not be re-requested.

In this change we make sure to reset those season rows back to UNKNOWN.

Disclosure: I used AI tooling to help investigate the root cause and draft the patch, though I reviewed it myself and questioned it heavily. I hit the bug myself on my own instance and verified both the reproduction and the fix by hand.

How Has This Been Tested?

I ran the reproduction which has been stated under #3278 with 3.3.0 as well as this branch, on my own live instance with real Sonarr.

Stock 3.3.0: after deleting the request the season row stayed PROCESSING in the db and the UI disabled the season toggle.
On this branch: after deleting the request the season toggle was off and enabled.

Also ran typecheck, lint and the full test suite on this branch (133/133)

Screenshots / Logs (if applicable)

Stock 3.3.0:
image

^ Stuck in PROCESSING

This branch:
image

^ Back to UNKNOWN

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • Bug Fixes
    • Improved TV media status updates after parent requests are removed or declined.
    • Reset seasons with no remaining active requests to an appropriate unknown state, preventing outdated pending or processing statuses.

Seasons left PENDING/PROCESSING after their request was deleted could never be re-requested ("No
seasons available to request"). Mirrors the existing decline-path season reset.

fix seerr-team#3278
@Knat-Dev
Knat-Dev requested a review from a team as a code owner July 21, 2026 20:07
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c849de30-a5bc-4205-a40a-ac9815ec73cb

📥 Commits

Reviewing files that changed from the base of the PR and between bf5323f and 1e16bc8.

📒 Files selected for processing (1)
  • server/subscriber/MediaRequestSubscriber.ts

📝 Walkthrough

Walkthrough

handleRemoveParentUpdate now loads media season relations and resets TV seasons stuck in PENDING or PROCESSING when no remaining active request covers them.

Changes

TV season status recalculation

Layer / File(s) Summary
Recalculate inactive TV seasons
server/subscriber/MediaRequestSubscriber.ts
The removal handler loads request and media seasons, identifies active seasons by request type, and changes uncovered pending or processing seasons to UNKNOWN.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • seerr-team/seerr#3276 — Reports stale TV season statuses after request deletion, which this change resets.

Possibly related PRs

  • seerr-team/seerr#3064 — Also updates media status recalculation in MediaRequestSubscriber after request removal.

Suggested reviewers: fallenbagel, 0xsysr3ll

Poem

I’m a rabbit with seasons to clear,
No stuck status shall linger here.
Pending hops to Unknown’s door,
Processing blocks requests no more.
Fresh TV requests can bloom once more!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately describes the main change: resetting orphaned season statuses after request deletion.
Linked Issues check ✅ Passed The code resets orphaned TV seasons from PENDING or PROCESSING to UNKNOWN, matching issue #3278's re-requestability requirement.
Out of Scope Changes check ✅ Passed The diff stays focused on request-deletion season cleanup with no obvious unrelated changes.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@fallenbagel fallenbagel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also needs tests, since there aren't any in this PR. request.test.ts's delete-restoration suite is movie-only, which is how this bug shipped in the first place. At minimum:

  • request-covered season at PROCESSING, delete, reset to UNKNOWN, re-request succeeds
  • season NOT covered by the deleted request at PROCESSING, delete, untouched
  • second active request covering the season, delete the first, stays PROCESSING
  • delete a non-4K request, status4k untouched

!activeSeasonNumbers.has(season.seasonNumber)
) {
season[statusKey] = MediaStatus.UNKNOWN;
await manager.save(season);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Batch this instead of a save per row.

Suggested change
await manager.save(season);
changedSeasons.push(season);

with const changedSeasons: Season[] = []; declared before the loop, and if (changedSeasons.length) await manager.save(changedSeasons); after it closes.

Comment on lines +1007 to +1023
const statusKey = entity.is4k ? 'status4k' : 'status';
const activeSeasonNumbers = new Set(
fullMedia.requests
.filter(
(request) =>
request.is4k === entity.is4k &&
request.status !== MediaRequestStatus.COMPLETED &&
request.status !== MediaRequestStatus.DECLINED
)
.flatMap((request) => request.seasons.map((s) => s.seasonNumber))
);

for (const season of fullMedia.seasons) {
if (
(season[statusKey] === MediaStatus.PENDING ||
season[statusKey] === MediaStatus.PROCESSING) &&
!activeSeasonNumbers.has(season.seasonNumber)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sweeps every PENDING/PROCESSING season with no remaining active request, including seasons whose state came from arr scan and had nothing to do with this deleted request. Example: S1 requested, S2/S3 mid-download via Sonarr, delete the S1 request and S2/S3 flip back to requestable until the next scan runs, which could be hours away or disabled.

entity.seasons is eager-loaded, so scope this to what the deleted request actually covered:

Suggested change
const statusKey = entity.is4k ? 'status4k' : 'status';
const activeSeasonNumbers = new Set(
fullMedia.requests
.filter(
(request) =>
request.is4k === entity.is4k &&
request.status !== MediaRequestStatus.COMPLETED &&
request.status !== MediaRequestStatus.DECLINED
)
.flatMap((request) => request.seasons.map((s) => s.seasonNumber))
);
for (const season of fullMedia.seasons) {
if (
(season[statusKey] === MediaStatus.PENDING ||
season[statusKey] === MediaStatus.PROCESSING) &&
!activeSeasonNumbers.has(season.seasonNumber)
const statusKey = entity.is4k ? 'status4k' : 'status';
const removedSeasonNumbers = new Set(
entity.seasons.map((s) => s.seasonNumber)
);
const activeSeasonNumbers = new Set(
fullMedia.requests
.filter(
(request) =>
request.is4k === entity.is4k &&
request.status !== MediaRequestStatus.COMPLETED &&
request.status !== MediaRequestStatus.DECLINED
)
.flatMap((request) => request.seasons.map((s) => s.seasonNumber))
);
for (const season of fullMedia.seasons) {
if (
(season[statusKey] === MediaStatus.PENDING ||
season[statusKey] === MediaStatus.PROCESSING) &&
removedSeasonNumbers.has(season.seasonNumber) &&
!activeSeasonNumbers.has(season.seasonNumber)

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.

Deleting a TV request can leave its seasons stuck in Processing and block re-requests

2 participants