Ombi builds GET /api/v2/Requests/recentlyRequested TV rows from child requests, so requestId is a child request id — and upstream persists the provider id as the child PK for new-request children. The value is therefore provider-shaped (TVDB/TMDB) while the parent request id never appears in the payload. Consumers following target into read_requests get hit upstream 500s.
Fix
read_requests recent: TV rows resolve through one bounded v1 parent scan — exact child-id match against embedded childRequests first (authoritative), then a tvDbId/externalProviderId fallback for versions that put the provider id in requestId directly. The child id stays available as an ombi_tv_child identifier; parent provider ids land under tvdb/tmdb/imdb; unresolvable rows emit target.id 0 with a warning — never a fabricated or provider-shaped target.
eachTVRequestParent now decodes the real {"collection":[...],"total":N} wrapper (it was dead on live — this also repairs the #7 details overlay and #10 search fallback on real instances).
read_requests children/list: provider ids are surfaced from the embedded parentRequest record; the tv_child target id remains the true upstream child PK (provider-shaped by design, callable for child-scoped operations).
read_discover requested fallback resolves TV rows to tv_parent like the primary browse path.
read_media details: the #7 overlay gate now treats requestId: 0 as absent (upstream sends 0, not a missing field), so the request-state overlay actually fires on live.
Mock fixtures updated to the real wire shapes; contract tests cover resolved, real-child-id, and unresolvable rows; a live test proves recent tv_parent targets are callable via get.
Verification
gofmt -l ., go vet ./..., go test ./..., go test -tags integration ./internal/integration_test/, CGO_ENABLED=0 go build ./... — all clean.
Live suite: new TestLiveRecentTVParentTargetIsCallable passes; three unrelated live failures reproduce identically on develop (upstream v2 TV list 500, read_users 404→500, popular-browse payload over the 8 MiB read budget).
Closes #11
## Root cause
Ombi builds `GET /api/v2/Requests/recentlyRequested` TV rows from **child** requests, so `requestId` is a child request id — and upstream persists the provider id as the child PK for new-request children. The value is therefore provider-shaped (TVDB/TMDB) while the parent request id never appears in the payload. Consumers following `target` into `read_requests get` hit upstream 500s.
## Fix
- `read_requests recent`: TV rows resolve through one bounded v1 parent scan — exact child-id match against embedded `childRequests` first (authoritative), then a `tvDbId`/`externalProviderId` fallback for versions that put the provider id in `requestId` directly. The child id stays available as an `ombi_tv_child` identifier; parent provider ids land under `tvdb`/`tmdb`/`imdb`; unresolvable rows emit `target.id` 0 with a warning — never a fabricated or provider-shaped target.
- `eachTVRequestParent` now decodes the real `{"collection":[...],"total":N}` wrapper (it was dead on live — this also repairs the #7 details overlay and #10 search fallback on real instances).
- `read_requests children`/`list`: provider ids are surfaced from the embedded `parentRequest` record; the `tv_child` target id remains the true upstream child PK (provider-shaped by design, callable for child-scoped operations).
- `read_discover` requested fallback resolves TV rows to `tv_parent` like the primary browse path.
- `read_media details`: the #7 overlay gate now treats `requestId: 0` as absent (upstream sends 0, not a missing field), so the request-state overlay actually fires on live.
- Mock fixtures updated to the real wire shapes; contract tests cover resolved, real-child-id, and unresolvable rows; a live test proves recent `tv_parent` targets are callable via `get`.
## Verification
- `gofmt -l .`, `go vet ./...`, `go test ./...`, `go test -tags integration ./internal/integration_test/`, `CGO_ENABLED=0 go build ./...` — all clean.
- Live suite: new `TestLiveRecentTVParentTargetIsCallable` passes; three unrelated live failures reproduce identically on `develop` (upstream v2 TV list 500, `read_users` 404→500, popular-browse payload over the 8 MiB read budget).
Live Ombi serves GET /api/v1/Request/tv/{count}/{pos}/1/0/0 as
{"collection":[...],"total":N}, not a bare array, so decodeArray made
every bounded parent scan fail on real instances — the #7 details
overlay and the #10 search fallback only worked against the mock's
unrealistic bare-array fixture. Decode the documented wrapper shape
(tolerating bare arrays) and fix the mock to serve the real shape so
tests exercise what production sees.
Closes#11. Ombi builds recentlyRequested TV rows from child requests,
so requestId is a child id — and upstream persists the provider id as
the child PK for new-request children, so the value is provider-shaped
(TVDB/TMDB) while the parent request id never appears in the payload.
Consumers following target into read_requests get hit upstream 500s.
TV rows now resolve through one bounded v1 parent scan: an exact
child-id match against embedded childRequests (authoritative), then a
provider-id fallback against tvDbId/externalProviderId covering
versions that put the provider id in requestId directly. The child id
is preserved as an ombi_tv_child identifier, provider ids land under
tvdb/tmdb/imdb, and unresolvable rows emit target.id 0 with a warning
rather than a fabricated or provider-shaped target.
ChildRequests carries no top-level provider ids upstream — they live
on the embedded parentRequest navigation property — so children/list
items emitted empty identifiers. Read them from the embedded record;
the child target id itself stays the true child PK (provider-shaped
for first-request children by upstream design, and callable for
child-scoped operations). Mock child rows and the new
/Request/tv/{id}/child fixture now match the real wire shape.
The recentlyRequested fallback emitted tv_child targets for TV rows
while the primary requested-browse path emits tv_parent — and a
tv_child target is a dead end for read_requests get/children. Resolve
the child request id to the parent via the same bounded scan so both
paths agree; rows that fail resolution keep the truthful tv_child
target.
Upstream Search/tv/info responses send requestId 0 (not a missing
field) for shows without a linked request, so the #7 request-state
overlay never fired on live instances — requested shows reported
requested:false. Now that the parent scan decodes the real collection
wrapper, gating on requestId nil-or-0 lets the overlay actually run.
The schema contract claimed recent target.id is requestId for every
kind — upstream TV rows actually carry a child request id there. The
docs now describe the bounded parent-scan resolution, the
ombi_tv_child identifier, and the id-0-with-warning degradation. A
live test proves a recent tv_parent target is callable via get.
gronod
merged commit 5b1be698d6 into develop2026-09-19 16:22:50 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #11
Root cause
Ombi builds
GET /api/v2/Requests/recentlyRequestedTV rows from child requests, sorequestIdis a child request id — and upstream persists the provider id as the child PK for new-request children. The value is therefore provider-shaped (TVDB/TMDB) while the parent request id never appears in the payload. Consumers followingtargetintoread_requests gethit upstream 500s.Fix
read_requests recent: TV rows resolve through one bounded v1 parent scan — exact child-id match against embeddedchildRequestsfirst (authoritative), then atvDbId/externalProviderIdfallback for versions that put the provider id inrequestIddirectly. The child id stays available as anombi_tv_childidentifier; parent provider ids land undertvdb/tmdb/imdb; unresolvable rows emittarget.id0 with a warning — never a fabricated or provider-shaped target.eachTVRequestParentnow decodes the real{"collection":[...],"total":N}wrapper (it was dead on live — this also repairs the #7 details overlay and #10 search fallback on real instances).read_requests children/list: provider ids are surfaced from the embeddedparentRequestrecord; thetv_childtarget id remains the true upstream child PK (provider-shaped by design, callable for child-scoped operations).read_discoverrequested fallback resolves TV rows totv_parentlike the primary browse path.read_media details: the #7 overlay gate now treatsrequestId: 0as absent (upstream sends 0, not a missing field), so the request-state overlay actually fires on live.tv_parenttargets are callable viaget.Verification
gofmt -l .,go vet ./...,go test ./...,go test -tags integration ./internal/integration_test/,CGO_ENABLED=0 go build ./...— all clean.TestLiveRecentTVParentTargetIsCallablepasses; three unrelated live failures reproduce identically ondevelop(upstream v2 TV list 500,read_users404→500, popular-browse payload over the 8 MiB read budget).Live Ombi serves GET /api/v1/Request/tv/{count}/{pos}/1/0/0 as {"collection":[...],"total":N}, not a bare array, so decodeArray made every bounded parent scan fail on real instances — the #7 details overlay and the #10 search fallback only worked against the mock's unrealistic bare-array fixture. Decode the documented wrapper shape (tolerating bare arrays) and fix the mock to serve the real shape so tests exercise what production sees.ChildRequests carries no top-level provider ids upstream — they live on the embedded parentRequest navigation property — so children/list items emitted empty identifiers. Read them from the embedded record; the child target id itself stays the true child PK (provider-shaped for first-request children by upstream design, and callable for child-scoped operations). Mock child rows and the new /Request/tv/{id}/child fixture now match the real wire shape.