name: reference-overseerr-seerr-request-pipeline description: "seerr (Overseerr fork) movie/TV request pipeline on media-server:5055; the 4K-default-profile trap that silently blocks all downloads, plus the fix recipe" metadata: node_type: memory type: reference
seerr (Overseerr) request pipeline + the 4K-default trap¶
URL: https://mkdocs.justinsforge.com/memory/general/reference_overseerr_seerr_request_pipeline/
What it is: seerr (Overseerr fork, ghcr.io/seerr-team/seerr:latest) runs on media-server CT 102, published at :5055. Family streamers (Krystal, Michael, Miles) request movies/TV; seerr hands approved requests to Radarr (:7878) / Sonarr, which search Prowlarr indexers and download via qBit. This is the video request system; shelfarr is the separate book equivalent.
API access (no dedicated secret file):
- seerr API key: docker exec seerr cat /app/config/settings.json | python3 -c 'import sys,json;print(json.load(sys.stdin)["main"]["apiKey"])'
- Radarr API key: docker exec radarr cat /config/config.xml | grep -o '<ApiKey>[^<]*' | sed 's/<ApiKey>//'
- Recent requests: GET http://127.0.0.1:5055/api/v1/request?take=8&sort=added (header X-Api-Key). Request status: 1=Pending,2=Approved,3=Declined. Media status: 1=Unknown,2=Pending,3=Processing,4=Partial,5=Available.
THE TRAP (diagnosed 2026-06-07): seerr's default Radarr server ("Movies", id 0) had activeProfileId=5 = Ultra-HD (2160p/4K only) while is4k=false. Every movie request inherited the 4K-only profile; older/catalog titles have no 4K release, so Radarr rejected every candidate ("not wanted in profile") and the request sat in Processing forever. Silent: no error surfaced to the requester. Symptom = "I requested X and nothing happened." Secondary blocker: Radarr quality definitions had a minimum size (e.g. 6.4 GB on Bluray-1080p) rejecting normal 1080p rips ("1.7 GB is smaller than minimum allowed").
Letting users choose resolution: the request-modal quality/resolution picker is the Request Advanced permission. REQUEST_ADVANCED bit = 8192 (NOT 2048 — 2048 is REQUEST_4K_MOVIE, useless without a 4K server; granting it does nothing visible). Admins have it implicitly (ADMIN bit = 2); regular users don't, so they see only a plain "Request" button. To grant: PUT /api/v1/user/{id} with {"permissions": <current | 8192>}. Family users set to 42144 on 2026-06-07 (Krystal id4, Michael id3, Miles id2). seerr main.defaultPermissions also bumped 33952→42144 same day so NEW users get the picker automatically (set via POST /api/v1/settings/main with only {"defaultPermissions":42144} — POSTing the full main object 400s on read-only fields like apiKey). Overseerr permission bits of note: ADMIN=2, REQUEST=32, AUTO_APPROVE=128, REQUEST_4K=1024, REQUEST_4K_MOVIE=2048, REQUEST_4K_TV=4096, REQUEST_ADVANCED=8192, REQUEST_VIEW=16384. Independent of the default profile (default makes no-touch requests work; the perm lets them pick resolution per title).
Radarr quality profiles (ids): 1 Any, 2 SD, 3 HD-720p, 4 HD-1080p, 5 Ultra-HD, 6 HD 720p/1080p, 7 HD Bluray + WEB (good default).
FIX RECIPE:
1. Set seerr default movie profile to HD. GET /api/v1/settings/radarr, take server id 0 body, drop the id field (read-only, else 400), set activeProfileId=7 + activeProfileName="HD Bluray + WEB", PUT /api/v1/settings/radarr/0.
2. Clear Radarr min sizes: per-definition PUT /api/v3/qualitydefinition/{id} with minSize=0 (the bulk /qualitydefinition/update with null returns 202 but does NOT persist; loop per-item with 0).
3. Rescue already-stuck movies: for each Radarr movie on profileId 5 with hasFile=false, PUT /api/v3/movie/{id} with qualityProfileId=7, then POST /api/v3/command {"name":"MoviesSearch","movieIds":[id]}.
4. Verify via GET /api/v3/queue and GET /api/v3/release?movieId={id} (shows rejections explicitly).
Not-a-bug case: brand-new theatrical releases (e.g. a 2026 film) may only have CAM/TELESYNC, which Radarr correctly rejects; they auto-grab once a proper WEB/Bluray release lands.
TV / Sonarr side (2026-06-07): Same two traps as Radarr existed on Sonarr and were fixed: (1) seerr default Sonarr profile was Ultra-HD (4K-only) → changed to id 6 "HD - 720p/1080p"; (2) Sonarr quality definitions had minimum sizes (e.g. 2.1GB for a 43min ep) rejecting normal 1080p episodes → cleared on all 22 defs. Sonarr prefers season packs on a season search, falls back to individual episodes. Monk S1 case: not a config bug — Monk Season 1 (2002) has ~zero seeders on all public torrent indexers (S7/S8 well-seeded, S1 dead). Indexers configured: 6 public torrent (1337x, EZTV, Knaben, LimeTorrents, TPB, TorrentGalaxy) + YTS (movies) + MAM (books, private). All protocol: torrent. Gap = no Usenet → old/obscure content unavailable; recommended fix is adding Usenet (SABnzbd + provider + NZB indexer) for retention-based downloads, or a private TV tracker. Monk series profile set to 6 (allows 720p) so it grabs if a 720p release ever appears.
x265 allowed (2026-06-07): the default profile (7 "HD Bluray + WEB") originally banned x265 (HD) at -10000 (TRaSH "Golden Rule HD" group via Recyclarr), which silently blocked grabs for older catalog titles whose well-seeded releases are mostly x265. Fix: commented out the Golden Rule HD group in /opt/stacks/media/recyclarr/configs/hd-bluray-web.yml (so Recyclarr stops managing/re-banning it) AND set x265 (HD) score to 0 directly on Radarr profile 7 (commenting alone doesn't reset an already-applied score; recyclarr leaves unreferenced CFs untouched). Recyclarr runs @daily; kept bans: LQ, upscaled, AV1, 3D, BR-DISK, bad dual groups, cam/telesync. To re-ban x265, uncomment the group + re-sync.
Recurring grab/import failures (re-confirmed 2026-06-12, Peanuts batch)¶
Three separate gotchas conspired to make requested movies "do nothing." All three hit again on the 2026-06-12 Peanuts request:
- Min-size filter REGRESSED. Radarr quality definitions were back to 4.0-4.4 GB minimums (the 2026-06-07 clear had reverted, suspect Recyclarr
@dailyor a Radarr update re-seeding defaults). Effect: every well-seeded small rip (1.3-1.6 GB x264, 13-25 seeders) rejected as "smaller than minimum allowed"; Radarr fell back to the only large releases (5.9 GB), which had ~0-5 seeders and stalled. Fix is the same per-item loop:GET /api/v3/qualitydefinition, for each idPUT /api/v3/qualitydefinition/{id}withminSize=0. Verify none remain >0 afterward. This WILL regress again; check min sizes first whenever a grab stalls on a low-seed big torrent. - qBit queues new grabs and never starts them. New torrents land in state
queuedDL(qBit max-active-downloads queue), 0% / 0 seeds / 0 speed, indefinitely. Fix: force-start them. qBit hasWebUI\LocalHostAuth=false, so from inside the container no auth needed:docker exec qbittorrent curl -s http://127.0.0.1:8080/api/v2/torrents/infoto get hashes, then.../api/v2/torrents/setForceStart --data "hashes=H1|H2&value=true". (Radarr's stored qBit password is masked in the API; use the localhost bypass instead of hunting for creds.) - Import command mismatch. A completed tracked download imports on
RefreshMonitoredDownloads, NOTDownloadedMoviesScan. The drop-folder scan only picks up untracked files; a torrent Radarr already has in its queue staystrackedDownloadState: downloadinguntil Refresh re-polls qBit, sees 100%, and imports. Symptom: qBit showsforcedUP/100% butGET /api/v3/moviestillhasFile=false. Fix:POST /api/v3/command {"name":"RefreshMonitoredDownloads"}. Radarr runs this on its own ~1-min interval, so it self-heals eventually; trigger manually to not wait.
Useful while diagnosing: GET /api/v3/release?movieId={id} returns ALL candidates with seeders + rejections (sort by seeders desc to see what's available vs why each was rejected). To swap a stalled junk torrent: DELETE /api/v3/queue/{id}?removeFromClient=true&blocklist=true then POST MoviesSearch.
Download/ETA notifications — the RELAY is the real system (not seerr built-in)¶
Primary notifier = seerr_email_webhook.py relay (built 2026-05-27), runs on media-server as seerr-email-webhook.service (listens 0.0.0.0:9876), fed by Radarr + Sonarr "Connect" webhooks ("Email Relay" → http://192.168.86.74:9876, onGrab+onImport). Flow: Grab → wait 150s → query qBit ETA → email "downloading (ETA: X)"; Import → email "ready to watch on Plex"; Health → email Justin. Looks up the requester via Overseerr API and emails them. This is the "is it downloading / how long" system Justin remembers.
- Canonical source:
forge/scripts/integrations/seerr_email_webhook.py; deployed copy/opt/stacks/media/seerr_email_webhook.py. Deploy =sudo teethe file over SSH, thensystemctl restart seerr-email-webhook.service. - 3-email flow: grab confirmation (immediate) → progress+ETA (after
GRAB_DELAY_SECONDS=150s) → "ready to watch" (on import). ETA pulled from Radarr/Sonarr/api/v3/queuetimeleft. - Completed-vs-stuck guard (patched 2026-06-07): the grab thread reads an empty download queue as "done" only if
already_completed(tmdb_id)(import event recorded inRECENTLY_COMPLETEDor RadarrhasFile); otherwise "stalled." Prevents the old bug where a download finishing before the 150s timer triggered a false "stalled/unavailable" email after the user already got the "ready" email. A title already on disk (e.g. from prior testing) re-imports with NO grab event, so it only sends the "ready" email — expected, not a fault. - SMTP creds externalized (2026-06-07):
/opt/stacks/media/seerr-smtp.env(chmod 600), loaded viaEnvironmentFile=in the unit; script readsos.environ["SMTP_PASS"]. Was previously hardcoded (fijl nmqy errs kmum) — Justin deleted that Gmail app password, relay died with535 BadCredentialson every grab. Lesson: a rotated/deleted Gmail app password silently kills every client that hardcoded it; keep creds in one env file. The live "Overseer" app password ([email protected]) drives both this relay and (if ever enabled) the seerr built-in agent. - seerr built-in email agent is intentionally DISABLED — the relay is the sole notifier. Enabling the built-in too = duplicate "ready" emails. (Justin chose relay-only 2026-06-07.)
Email notifications (SMTP) — seerr built-in agent (secondary, normally OFF)¶
Two layers, BOTH required for a user to receive mail:
1. Global email agent: POST /api/v1/settings/notifications/email (note: POST not PUT, PUT=405). Config: smtpHost=smtp.gmail.com, smtpPort=587, secure=false, requireTls=true (STARTTLS), authUser/emailFrom/authPass. Creds in ~/.forge-secrets/seerr-smtp.env (chmod 600). GOTCHA: the "Overseer" Gmail app password belongs to [email protected] (Workspace), NOT [email protected] — personal gmail returns 535-5.7.8 BadCredentials. Verify which account an app password belongs to with a direct smtplib .login() test before assuming.
2. Per-user types: POST /api/v1/user/{id}/settings/notifications with emailEnabled=true + notificationTypes.email=<bitmask>. A user with emailEnabled=true but notificationTypes.email=0/{} gets nothing. Family users set to 204 (approved 4 + available 8 + declined 64 + auto_approved 128); admin user 1 = 156.
Notification type bits: PENDING=2, APPROVED=4, AVAILABLE=8, FAILED=16, TEST=32, DECLINED=64, AUTO_APPROVED=128, ISSUE_CREATED=256, ISSUE_COMMENT=512, ISSUE_RESOLVED=1024, ISSUE_REOPENED=2048, AUTO_REQUESTED=4096. Global agent types = union the agent may emit.
Settings-wipe gotcha (2026-06-07): seerr serializes the entire settings.json on any settings save. After changing one section via API/UI, the email block came back empty (SMTP + app password lost, only recoverable by re-entering). Backups (settings.json.bak.<date>, settings.old.json) are version-migration snapshots and may predate your notification config. Always read back notification settings after any seerr settings change, and keep SMTP creds in the secrets file so they can be re-pushed.
Related: [[reference-media-server-stack]], [[reference-myanonamouse]], [[reference-plex-state]], [[reference-google-oauth-architecture]].
[Claude Code]