Skip to content

link: transient and unknown provider codes must not be cached as permanent - #369

Open
radamhub wants to merge 1 commit into
sirrobot01:betafrom
radamhub:fix/link-transient-error-category
Open

radamhub wants to merge 1 commit into
sirrobot01:betafrom
radamhub:fix/link-transient-error-category

Conversation

@radamhub

@radamhub radamhub commented Jul 30, 2026

Copy link
Copy Markdown

📌 Description

  • One transient provider error makes a file unreadable for the rest of the process lifetime, because the failure is cached and only a restart clears it. This is the mechanism behind "playback works, then stops until I restart the container".

Reported in #179.


Target Branch Check (IMPORTANT)

  • I confirm this PR is targeting the correct branch

Expected target:

  • beta (for features)

Changes Made

In ErrorCodeToLinkError (pkg/manager/link/errors.go):

  • 429 and 503: CategoryRetryableCategoryRefetchable. On the link-validation path only ShouldDisableAccount() and ShouldRefetch() are consulted, and CategoryRetryable is acted on by neither, so the error falls through and the failure is memoised against the download URL. When a provider derives that URL deterministically the cache key never rotates, which makes a rate limit exactly as permanent as a hard failure.
  • Added 500, 502, 504 as Refetchable for the same reason.
  • default: CategoryPermanentCategoryRefetchable. An unrecognised code is not evidence of permanent failure, and CategoryPermanent means neither retry nor refetch, so a single transient 400 poisons the file until the process restarts.
  • Genuinely permanent codes are unchanged: 401/unauthorized, 404, link_not_found, file_not_available, and the account/quota codes.
  • Added pkg/manager/link/errors_category_test.go asserting both directions.

Refetchable is the right category here because it is the only one that drops the memoised entry and returns a fresh link, and it does so without re-validating, so it cannot loop.


Testing

  • Tested locally

Steps:

  1. go test ./pkg/manager/link/ -run TestTransientCodesAreRefetchable -v passes with the change. Without it, 400 and unknown codes are reported permanent and 429/503 do not refetch.
  2. Ran the patched build for several days on a ~4000-file TorBox library. Before the change, a single transient 502 during validation left files unreadable while the very same HEAD request returned 200 when retried by hand. I reproduced that three times. After the change, those files recover on their own without restarting the container.
  3. Verified the permanent codes still terminate immediately: a real 404 is not retried in a loop.

Risks / Notes

  • Broadens what gets a second chance, so a genuinely dead link now costs one extra refetch before failing. That refetch does not re-validate, so there is no loop and no added latency on the happy path.
  • Behaviour for permanent codes is unchanged, which the test pins down explicitly to guard against over-correcting.
  • No config, API or on-disk format changes.
  • Independent of the streaming PR (different package, different code path); order does not matter, though the streaming one is the smaller review.

Checklist

  • Code builds successfully
  • No console/log errors
  • Reviewed my own code
  • Target branch is correct

…anent

ErrorCodeToLinkError maps 429/503 to CategoryRetryable and every
unrecognised code to CategoryPermanent. On the link-validation path only
ShouldDisableAccount() and ShouldRefetch() are consulted, so a Retryable
error is acted on by neither: it falls through and the failure is memoised
against the download URL. When a provider derives that URL deterministically
the cache key never rotates, so a rate limit becomes as permanent as a hard
failure and the file stays unreadable for the rest of the process lifetime.

Map 429/503 and 500/502/504 to Refetchable, and default unknown codes to
Refetchable as well: an unrecognised code is not evidence of permanent
failure. Refetchable is the only category that drops the memoised entry and
returns a fresh link, and it does not re-validate, so it cannot loop.
Genuinely permanent codes (401/404/link_not_found/file_not_available) are
unchanged.

This is the mechanism behind reports where playback works, then stops, and
only restarting the container fixes it.

Adds a unit test asserting both directions: transient codes refetch,
permanent codes stay permanent.
@radamhub
radamhub force-pushed the fix/link-transient-error-category branch from dcfa81e to db5a8fd Compare July 30, 2026 15:58
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.

1 participant