Retry transient NFT download failures instead of persisting them for good - #3060
Retry transient NFT download failures instead of persisting them for good#3060jlobue10 wants to merge 1 commit into
Conversation
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a3b8bcc. Configure here.
|
ckise and reooen for base branch change |
|
@jlobue10 can you retarget this to |
Will do |
…good The media cache persists every failed download as an ERROR sidecar and, apart from timeouts (retried once per session) and aborts, never fetches that URL again. Gateway conditions that clear on their own therefore poison an NFT until the whole cache is cleared: an HTTP 504 recorded while nftstorage.link was down stays a 504 after the host came back (it now redirects to ipfs.io), and the Cloudflare bot challenge that ipfs.io intermittently answers with — an HTTP 403 "Just a moment..." page, or net::ERR_BLOCKED_BY_RESPONSE when Chromium rejects that page's Cross-Origin-Resource-Policy header — is recorded as a permanent failure although the very next request usually succeeds. The NFT then shows "Preview is not available" in every later session (Chia-Network#3058). Classify timeouts, HTTP 5xx, 403/408/425/429 and net::ERR_* failures as transient. A persisted transient failure is retried once per session, as timeouts already were, and again whenever the retry delay (10 minutes) has elapsed since it was recorded, so a gateway hiccup no longer blanks an NFT until the GUI restarts. Missing resources (404/410) and the size cap keep their settled behaviour. Fixes Chia-Network#3058 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ni9VuLznDQTZh2V8aBis76
…ough The ipfs:// gateway option always used the public ipfs.io gateway. That gateway (and nftstorage.link, which now redirects to it) sits behind Cloudflare bot management and intermittently answers the GUI's requests with a 403 challenge page it cannot solve, while other public gateways and a local IPFS node serve the same files without trouble (Chia-Network#3058). Users had no way to route around it. Add an "IPFS gateway" preference under the gateway option. Whatever the user enters is normalized to a path-gateway base (`https://dweb.link`, `https://dweb.link/ipfs` and `https://dweb.link/ipfs/` all become `https://dweb.link/ipfs/`); https is required except for a plain-http gateway on this machine, which is how a local Kubo node serves by default. Anything unusable falls back to the default rather than producing URLs that cannot be fetched. The main process reads the persisted value at every network call site, as it already does for the option itself. Changing the gateway takes effect immediately: hash verification and failed metadata fetches re-run, and the cache re-requests an ipfs failure recorded under a different gateway right away instead of waiting out the transient-error delay — the sidecar now records which gateway a failed ipfs request went through. The dapp dialog's oversized-image fallback skips a plain-http gateway URL, which its CSP could not embed. Stacked on Chia-Network#3060, which introduces the retry delay this builds on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ni9VuLznDQTZh2V8aBis76
a3b8bcc to
3e4b097
Compare
|
bugbot review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3e4b097. Configure here.
Fixes #3058
Problem
The NFT media cache persists every failed download as an
ERRORsidecar and, apart from timeouts (retried once per session) and aborts, never fetches that URL again. That is the right call for a resource that is gone (404), but two conditions that clear on their own were being recorded the same way, and each of them leaves the NFT on "Preview is not available" in every later session until the whole cache is cleared:HTTP error: 504—nftstorage.linkwas down for weeks and answered 504 for everything. It is back now as a redirector toipfs.io(the reporter's URL resolves and hashes correctly), but any GUI that recorded the 504 keeps throwing it from the sidecar without ever re-requesting the file. That is why the reporter seescacheAPI:getChecksum: HTTP error: 504for a URL that curl fetches in under a second.net::ERR_BLOCKED_BY_RESPONSE—ipfs.io(whichnftstorage.linknow redirects to, and which theipfs://gateway option uses) sits behind Cloudflare bot management and intermittently answers Electron'snet.requestwith a 403 "Just a moment..." challenge page (cf-mitigated: challenge). The GUI cannot solve a JS challenge, so the file fails, and the very next request usually succeeds. That page carriesCross-Origin-Resource-Policy: same-origin, which Chromium turns intoERR_BLOCKED_BY_RESPONSEwhen the request has an initiator and into a plain HTTP 403 otherwise; both were persisted as permanent failures.I could not confirm the race in
NFTPreviewsuggested in the issue:getURIandgetChecksumshare the samefetchRemoteContentpromise for a URL (ongoingRequests), so the optimistic tile waits for the same download the verifier runs and cannot observe a different outcome for the same URI.Fix
isTransientDownloadErrorclassifies timeouts, HTTP 5xx, 403/408/425/429 andnet::ERR_*failures as transient.CacheManagerretries a persisted transient failure once per session (as it already did for timeouts) and again wheneverTRANSIENT_ERROR_RETRY_DELAY(10 minutes) has elapsed since the failure was recorded, so a gateway hiccup no longer blanks an NFT until the GUI restarts. Missing resources (404/410) and the size cap keep their settled behaviour; theIpfsGatewayDisabledErrorpath is untouched (it was never persisted).The retry decision uses the sidecar's existing
timestamp, so already-poisoned caches recover on their own after upgrading: the next visit to the NFT re-requests the file.Verification
nftstorage.linktoipfs.iois followed fine, butipfs.ioanswered a burst of requests with the Cloudflare challenge (HTTP 403,cf-mitigated: challenge, CORPsame-origin) before letting the same requests through minutes later. A local server returning the challenge page's headers reproducesnet::ERR_BLOCKED_BY_RESPONSEon both Electron versions whenever the request carries an initiator.HTTP error: 504fornftstorage.linkURLs recorded while the host was down, plus 12 ×HTTP error: 403from the same host.ERR_BLOCKED_BY_RESPONSE, retry within the session once the delay has elapsed, and a 404 staying settled across sessions.packages/guijest: 396/396. eslint and prettier clean; no new tsc errors in the touched files.🤖 Generated with Claude Code
Note
Low Risk
Changes are confined to Electron NFT download caching and retry policy; permanent-error and IPFS-gateway-disabled paths are unchanged.
Overview
NFT media cache errors from gateway outages, Cloudflare challenges, rate limits, and similar transient failures no longer stay poisoned until the user clears the whole cache.
isTransientDownloadError(indownloadFile) now classifies timeouts, HTTP 5xx, selected 4xx (403/408/425/429), andnet::ERR_*as retriable; 404/410 and size-cap errors remain permanent.CacheManageruses that helper instead of timeout-only checks, tracks session failures intransientFailureUrls, and exposes a 10-minuteTRANSIENT_ERROR_RETRY_DELAY: persisted transient ERROR sidecars are retried once per new session and again in-session once the sidecar timestamp is older than the delay. Immediate repeat fetches in the same session still do not hammer the host.Unit tests cover the classifier, no immediate retry for a fresh 504, cross-session retry for 504/403/
ERR_BLOCKED_BY_RESPONSE, in-session retry after the delay, and 404 staying settled.Reviewed by Cursor Bugbot for commit 3e4b097. Bugbot is set up for automated code reviews on this repo. Configure here.