v2.19.0 — Seerr permission errors, Schema Drift UX, continued #427 OOM mitigation
Added
- Seerr Test Connection now catches permission-misconfigured instances at setup time (closes #465). Previously
Test Connectionfor Seerr probed only/api/v1/status, which is taggedpublicin Seerr's openapi spec and skips theisAuthenticatedmiddleware. A Seerr instance whose API-key-backed user (user ID 1 by default, perseerr/server/middleware/auth.ts) had no usable permissions still passed the connection check — operators then configured the instance, opened Discover, and saw an empty page with no surfaced reason. After this release, Test Connection probes/api/v1/request/countafter/statussucceeds and returns a specific actionable error on 403 naming user ID 1 and the Settings → Users path in Seerr. Transient non-403 failures (5xx, 429) and network blips on the second probe fall through to success —/statusalready proved reachability, so the test doesn't fail on a flake. Non-Seerr services skip the second probe. Discovered while diagnosing #454, where a Seerr user with stripped permissions hadTest Connectionsucceed while every gated endpoint silently 403'd. - Discover surfaces upstream Seerr errors inline (closes #465).
DiscoverCarouselandDiscoverSearchResultspreviously rendered "Failed to load <title>" / "Search failed" with no underlying detail, identical to a "no results yet" state. TheSeerrApiError.messagewas already on the wire (via the central error handler →ApiError.messageon the frontend) but neither component displayed it. Now the underlying message renders inline below the generic banner — a Seerr 403 with"You do not have permission to access this endpoint"is visible in the carousel itself instead of requiring server-log access. Whitespace-only messages are ignored to avoid empty paragraphs; backward-compatible with any caller not yet updated to forward theerrorobject. - Schema Drift in Settings → System now explains what it is (closes #455). The section previously showed
Drift detectedwith+ field/~ field/- fieldbadges and no inline explanation, leading users to think it was reporting an error they needed to fix. Added a help tooltip on the header, an always-visible explanation paragraph inside the expanded panel, and a legend explaining the+ / ~ / -symbols (shown only when drift is actually present). The underlying detection logic is unchanged — Schema Drift remains a developer-facing diagnostic that surfaces upstream API evolution, not a user action item. - TRaSH migration notices for upstream PRs #2719 and #2721 (German/French unwanted-format groups). Upstream split language-specific unwanted-format CFs into dedicated
[Unwanted] Unwanted Formats Germanand[Unwanted] Unwanted Formats Frenchgroups on April 27–May 8. Each new group isdefault: "true"upstream, so the merger auto-adopts on next sync — users with German/French templates would see a new CF group appear with no explanation. Themigration-notices.tsregistry now has 4 new entries (RADARR + SONARR for each PR) that fire an info-level advisory when the user has the corresponding[Release Groups] German/Frenchanchor but lacks the new unwanted group. Notices fall silent once both groups are present. - Heap retainer-walk diagnostic script (#427).
apps/api/scripts/heap-retainer-walk.pywalks a V8 heap snapshot's retainer graph and surfaces accumulator chains by traversing back from the largest live objects. Used during the #427 OOM mitigation arc to identify which call paths were retaining memory across requests; reach for it before guessing on future OOM reports.
Fixed
- Continued #427 OOM mitigation across hunting, library-sync, dashboard-statistics, and diagnostics paths. After v2.18.5's sweep of
findManyreads, the next batch of memory work targets bulk-fetch paths, scheduler reentrancy, and operator visibility:- Library-sync now stream-parses the bulk JSON response (#448) and pop-drains
rawItemsprogressively instead of holding the full upstream payload + a normalized copy in memory simultaneously. For a 100k-item Sonarr response this halves peak heap during the sync window. - Hunting now stream-fetches the bulk catalog on every per-service hunt (#451) rather than allocating the full catalog up front, and slims
wantedrecords inside the paginator (#456) plus movie/album/book records on the upgrade-all path (#453). The hunting scheduler no longer accumulates large records across pages. - Library-sync startup-delays its first tick and uses adaptive concurrency (#452, #427) — the first sync after process start no longer races other startup-time work, and the worker pool now shrinks when heap pressure rises rather than running fixed-concurrency under memory pressure.
- Dashboard statistics now stream-aggregates the bulk library list (#449) instead of materializing the full list then aggregating — same peak reduction pattern as the calendar/history fix from 2.18.5.
- Diagnostics:
dump-heaphelper is now reachable on a running container (docker exec) so operators can capture snapshots without re-launching with--inspect; auto-snapshot triggers at 90% heap usage withHEAP_AUTO_SNAPSHOT=1, and the existingdump-heappath was verified end-to-end as part of the auto-snapshot fix.
- Library-sync now stream-parses the bulk JSON response (#448) and pop-drains
- Hunting scheduler now suppresses overlapping ticks (closes #457). Previously a slow hunt cycle could overlap the next scheduler tick, producing duplicate hunt attempts against the same Arr instance and inflating queue load. The scheduler now skips a tick if the previous one is still running, logging the skip at
warnso the operator can see if hunt latency is exceeding the cycle interval. - Seerr discover schema accepts
personandcollectionitems (closes #454, partial).seerrDiscoverResultSchema.mediaTypepreviously rejected anything other than"movie" | "tv". Seerr's/api/v1/discover/trendingreturns all fourmediaTypevalues, and a singlepersonitem in the response would throwUpstreamValidationError→ HTTP 502 → empty discover page. The schema now accepts the full union; non-movie/tv items are filtered at the server boundary so the publicSeerrDiscoverResultinterface stays narrow ("movie" | "tv"). Note: #454's reporter was actually hitting a Seerr permissions misconfiguration, not this bug — see the issue thread; the Test Connection / Discover error surfacing items above are the actual fix for that surface. - TRaSH Schema Drift no longer oscillates on naming presets (#463). The
fetchNamingDatapath keyed its schema fingerprint under one sharednamingPresetscategory for both Radarr and Sonarr — but the two services ship different top-level shapes, so the registry baseline oscillated every refresh and surfaced as constant intermittent-drift in Settings → System. Categories are now service-scoped (radarrNamingPresets/sonarrNamingPresets); each service keeps its own baseline and only reports drift against its own prior shape. - TRaSH legacy NAMING fetcher removed (#462). Two fetchers (
fetchNamingandfetchNamingData) walked the same upstream/sonarr/naming/and/radarr/naming/directories but applied incompatible schemas, producing twoSkipping invalid item 0 ... type: Invalid option: expected one of "movie"|"series"warnings per cache refresh. The legacy fetcher's output (TrashNamingScheme[]) had no consumers — all downstream code usedTrashNamingData[]from the newerfetchNamingDatapath. Removed the legacy fetcher, theNAMINGconfig type, thetrashNamingSchemeSchema, and theTrashNamingSchemeshared type. Each cache refresh now logs exactly two fewer false-positive validation warnings and quarantines exactly zero false-rejected items.
Dependencies
- Next.js, Hono, and fast-uri bumped to patch open security advisories (#446). Patch-level updates only; no behavior changes.
Docker image: khak1s/arr-dashboard:2.19.0 (and :latest after the Docker release workflow completes)