{"record":{"id":"ee8bf9bb7ee639f4","repo":"antiwork/gumroad","slug":"sorry-something-went-wrong-please-try-again-ee8bf9","errorCode":null,"errorMessage":"Sorry, something went wrong. Please try again.","messagePattern":"Sorry, something went wrong\\. Please try again\\.","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/components/Download/FileList.tsx","lineNumber":213,"sourceCode":"    <TrackClick eventName=\"download_click\" file={file}>\n      <NavigationButton href={downloadUrl}>Download</NavigationButton>\n    </TrackClick>\n  ) : null;\n  const streamUrl = file.stream_url;\n  const externalLinkUrl = file.external_link_url;\n  const mediaUrls = allMediaUrls[file.id] ?? [];\n  const fetchMediaUrls = async () => {\n    try {\n      if (isFetchingMediaUrls) return;\n      setIsFetchingMediaUrls(true);\n      const response = await request({\n        url: Routes.url_redirect_media_urls_path(purchaseInfo.token, {\n          params: { file_ids: [file.id] },\n        }),\n        method: \"GET\",\n        accept: \"json\",\n      });\n      if (!response.ok) throw new ResponseError();\n      const urls = typia.assert<Record<string, string[]>>(await response.json());\n      if (!urls[file.id]?.length) throw new ResponseError();\n      setAllMediaUrls((prev) => ({ ...prev, ...urls }));\n    } finally {\n      setIsFetchingMediaUrls(false);\n    }\n  };\n\n  const playAudio = () => {\n    if (!isShowingAudioDrawer) setPlayingAudioForId(file.id);\n    if (isShowingAudioDrawer && playingAudioForId === file.id) setPlayingAudioForId(null);\n    toggleAudioDrawer();\n  };\n\n  if (isMobileAppWebView && FileUtils.isAudioExtension(file.extension)) {\n    return <MobileAppAudioFileRow file={file} />;\n  }\n","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/components/Download/FileList.tsx#L195-L231","documentation":"fetchMediaUrls asks the server for streamable media URLs for a purchased file via GET url_redirect_media_urls_path(purchaseInfo.token, { file_ids }). A non-2xx response (4xx — 5xx/429 were already converted inside request()) throws a bare ResponseError, which the callers around lines 334/725 catch and report as 'Sorry, something went wrong. Please try again.'.","triggerScenarios":"The purchase token in the URL is expired, revoked, or malformed (401/404); the file id is not streamable media (422); the download session was invalidated (refund/chargeback) — any 4xx from the media-urls endpoint hits this throw.","commonSituations":"Buyer returns to an old download link whose token expired; purchase refunded so access was revoked; file replaced with a non-streamable type while the page was open; direct navigation with a truncated token (mailer line-wrap).","solutions":["Check the Network tab for the media-urls request: 401/404 points at the token, 422 at file eligibility.","If the token expired, send the buyer through the original download email/link again to mint a fresh token.","Verify the purchase still exists and is not refunded/disputed in the admin console.","Confirm the requested file id actually belongs to the purchase and is a streamable type.","Surface a more specific message than the generic string when the status is known to be an auth problem."],"exampleFix":"// before\nif (!response.ok) throw new ResponseError();\n\n// after\nif (!response.ok) throw new ResponseError(response.status === 401 ? 'This download link has expired. Use the link from your email.' : 'Could not load this file. Please try again.');","handlingStrategy":"try-catch","validationCode":"const looksLikeDownloadToken = (token: string | undefined): token is string =>\n  typeof token === 'string' && token.length > 10 && /^[A-Za-z0-9_-]+$/.test(token);\n\nif (!looksLikeDownloadToken(purchaseInfo?.token)) {\n  showAlert('This download link is not valid. Use the link from your receipt email.', 'error');\n  return;\n}","typeGuard":"const isResponseError = (e: unknown): e is ResponseError => e instanceof ResponseError;","tryCatchPattern":"try {\n  await fetchMediaUrls();\n} catch (e) {\n  assertResponseError(e);\n  showAlert(e.message, 'error');\n}","preventionTips":["Treat 401 from token-gated download endpoints as 'expired link', not 'something went wrong' — it is the dominant cause.","Regenerate download links through the original email flow instead of deep-linking old tokens.","Verify file ids sent in file_ids belong to the purchase before requesting media URLs.","Distinguish the two failure branches in fetchMediaUrls (HTTP vs empty payload) so the alert can be truthful."],"tags":["http","download","media-urls","token-expired","response-not-ok"],"backgroundTag":"http-request-failed","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}