Skip to content

v2.19.0 — Seerr permission errors, Schema Drift UX, continued #427 OOM mitigation

Choose a tag to compare

@Kha-kis Kha-kis released this 14 May 12:25
· 98 commits to main since this release

Added

  • Seerr Test Connection now catches permission-misconfigured instances at setup time (closes #465). Previously Test Connection for Seerr probed only /api/v1/status, which is tagged public in Seerr's openapi spec and skips the isAuthenticated middleware. A Seerr instance whose API-key-backed user (user ID 1 by default, per seerr/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/count after /status succeeds 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 — /status already 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 had Test Connection succeed while every gated endpoint silently 403'd.
  • Discover surfaces upstream Seerr errors inline (closes #465). DiscoverCarousel and DiscoverSearchResults previously rendered "Failed to load <title>" / "Search failed" with no underlying detail, identical to a "no results yet" state. The SeerrApiError.message was already on the wire (via the central error handler → ApiError.message on 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 the error object.
  • Schema Drift in Settings → System now explains what it is (closes #455). The section previously showed Drift detected with + field / ~ field / - field badges 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 German and [Unwanted] Unwanted Formats French groups on April 27–May 8. Each new group is default: "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. The migration-notices.ts registry 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/French anchor 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.py walks 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 findMany reads, 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 rawItems progressively 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 wanted records 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-heap helper 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 with HEAP_AUTO_SNAPSHOT=1, and the existing dump-heap path was verified end-to-end as part of the auto-snapshot fix.
  • 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 warn so the operator can see if hunt latency is exceeding the cycle interval.
  • Seerr discover schema accepts person and collection items (closes #454, partial). seerrDiscoverResultSchema.mediaType previously rejected anything other than "movie" | "tv". Seerr's /api/v1/discover/trending returns all four mediaType values, and a single person item in the response would throw UpstreamValidationError → HTTP 502 → empty discover page. The schema now accepts the full union; non-movie/tv items are filtered at the server boundary so the public SeerrDiscoverResult interface 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 fetchNamingData path keyed its schema fingerprint under one shared namingPresets category 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 (fetchNaming and fetchNamingData) walked the same upstream /sonarr/naming/ and /radarr/naming/ directories but applied incompatible schemas, producing two Skipping 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 used TrashNamingData[] from the newer fetchNamingData path. Removed the legacy fetcher, the NAMING config type, the trashNamingSchemeSchema, and the TrashNamingScheme shared 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)