{"record":{"id":"f915b9ff413e16df","repo":"antiwork/gumroad","slug":"something-went-wrong-f915b9","errorCode":null,"errorMessage":"Something went wrong.","messagePattern":"Something went wrong\\.","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/data/covers.ts","lineNumber":27,"sourceCode":"  const response = await request({\n    method: \"POST\",\n    accept: \"json\",\n    url: Routes.link_asset_previews_path(permalink),\n    data: {\n      asset_preview:\n        coverPayload.type === \"file\" ? { signed_blob_id: coverPayload.signedBlobId } : { url: coverPayload.url },\n    },\n  });\n\n  if (response.ok) {\n    const responseData = typia.assert<\n      { success: true; asset_previews: AssetPreview[] } | { success: false; error: string }\n    >(await response.json());\n    if (responseData.success) return responseData.asset_previews;\n    throw new ResponseError(responseData.error);\n  }\n\n  throw new ResponseError();\n};\n\nexport const deleteCover = async (permalink: string, coverId: string) => {\n  const response = await request({\n    method: \"DELETE\",\n    accept: \"json\",\n    url: Routes.link_asset_preview_path(permalink, coverId),\n  });\n\n  if (response.ok) {\n    const responseData = typia.assert<{ success: true; asset_previews: AssetPreview[] } | { success: false }>(\n      await response.json(),\n    );\n    if (responseData.success) return responseData.asset_previews;\n  }\n\n  throw new ResponseError();\n};","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/covers.ts#L9-L45","documentation":"ResponseError (\"Something went wrong.\") is Gumroad's generic API-failure error. This line in createCover is the fallthrough throw when POST /links/:permalink/asset_previews (link_asset_previews_path) answers non-2xx (the success:false-with-200 path throws earlier at line 24 with the server's message). request() already maps 5xx/429/network errors elsewhere, so this throw means a 4xx: expired session (401), wrong permalink (404), or a bad payload (422) such as a signed_blob_id from an upload that never committed or a url the server could not fetch.","triggerScenarios":"Creating a cover with a signedBlobId from an interrupted direct upload (the ActiveStorage blob was never committed, so the server rejects the id); passing a cover url that 404s or is private, since the server mirrors it; seller session/CSRF expired while the product editor sat open; permalink mismatch after the product was renamed mid-session.","commonSituations":"Product editor left open overnight so every save 401s; very large cover images failing the upload step but the stale blob id still being submitted; pasted S3 presigned/expiring URLs as the cover source; two tabs editing the same product with one renaming it.","solutions":["DevTools → Network: check the status of the POST .../asset_previews request.","401: reload the page to refresh the session and retry — the signed blob id survives a reload.","422: re-run the file upload flow to obtain a fresh signed_blob_id before calling createCover; do not reuse ids from failed uploads.","For url covers, verify the URL is public and reachable (curl -I) — the server fetches it server-side."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (coverPayload.type === \"file\" && !coverPayload.signedBlobId)\n  throw new Error(\"Upload the image first — the cover payload has no file reference.\");\nif (coverPayload.type === \"url\" && !/^https?:\\/\\//.test(coverPayload.url))\n  throw new Error(\"Cover URL must start with http(s)://\");\nawait createCover(permalink, coverPayload);","typeGuard":"const isCoverPayload = (p: unknown): p is CoverPayload =>\n  typeof p === \"object\" && p !== null &&\n  ((p as { type?: string }).type === \"file\" && typeof (p as { signedBlobId?: string }).signedBlobId === \"string\") ||\n  ((p as { type?: string }).type === \"url\" && typeof (p as { url?: string }).url === \"string\");","tryCatchPattern":"import { assertResponseError } from \"$app/utils/request\";\ntry {\n  const previews = await createCover(permalink, coverPayload);\n} catch (e) {\n  assertResponseError(e);\n  if (needsReauth()) { location.reload(); return; } // stale session is the common 401\n  showAlert(\"Couldn't save that cover. Re-upload the image and try again.\", \"error\");\n}","preventionTips":["Only call createCover with a signedBlobId from an upload step that completed successfully.","For url covers, check the URL is public (curl -I) before submitting — the server fetches it.","Save the editor soon after opening; long-idle sessions lose CSRF and every POST 401s."],"tags":["covers","upload","activestorage","product-editor","http","fetch"],"backgroundTag":"http-4xx-response","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}