{"record":{"id":"fcf96e829ee3d939","repo":"antiwork/gumroad","slug":"something-went-wrong-fcf96e","errorCode":null,"errorMessage":"Something went wrong.","messagePattern":"Something went wrong\\.","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/data/upsells.ts","lineNumber":136,"sourceCode":"    method: \"DELETE\",\n    accept: \"json\",\n    url: Routes.checkout_upsell_path(id),\n  });\n  const responseData = typia.assert<\n    { success: true; upsells: Upsell[]; pagination: PaginationProps } | { success: false; error: string }\n  >(await response.json());\n  if (!responseData.success) throw new ResponseError(responseData.error);\n\n  return responseData;\n};\n\nexport const pauseUpsell = async (id: string) => {\n  const response = await request({\n    method: \"POST\",\n    accept: \"json\",\n    url: Routes.checkout_upsell_pause_path(id),\n  });\n  if (!response.ok) throw new ResponseError();\n};\n\nexport const resumeUpsell = async (id: string) => {\n  const response = await request({\n    method: \"DELETE\",\n    accept: \"json\",\n    url: Routes.checkout_upsell_pause_path(id),\n  });\n  if (!response.ok) throw new ResponseError();\n};\n\nexport const getPagedUpsells = (page: number, query: string | null, sort: Sort<SortKey> | null) => {\n  const abort = new AbortController();\n  const response = request({\n    method: \"GET\",\n    accept: \"json\",\n    url: Routes.paged_checkout_upsells_path({ page, query, sort }),\n    abortSignal: abort.signal,","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/upsells.ts#L118-L154","documentation":"pauseUpsell (app/javascript/data/upsells.ts:130-137) POSTs Routes.checkout_upsell_pause_path(id) and throws generic ResponseError ('Something went wrong.') when response.ok is false. request() already converts 5xx, 429 (RateLimitError), and network failures, so this branch means a 4xx: 404 (upsell id no longer exists — deleted in another tab), 401 (expired session), or 403. Called from the pause/resume toggle in CheckoutDashboard/UpsellsPage.tsx:222.","triggerScenarios":"Toggling an upsell to paused when the row was deleted in another tab (404); pausing right after session expiry (401); pausing an upsell the seller no longer owns; double-toggling fast so the second POST races the first and hits a transitional state.","commonSituations":"Stale dashboard tables after multi-tab editing; expired sessions on long-open dashboards; race between the toggle handler and a concurrent delete; tests stubbing the pause route with non-2xx.","solutions":["Reload the upsells page and retry the pause on a current row id","Check the POST's status in the network tab (404 vs 401 vs 403) to pick the message","Disable the pause/resume toggle while a toggle is in flight","Catch with assertResponseError and show e.message; treat 404 as 'already gone' and refresh"],"exampleFix":"// components/CheckoutDashboard/UpsellsPage.tsx — reconcile on pause failure\ntry {\n  await pauseUpsell(selectedUpsell.id);\n} catch (e) {\n  assertResponseError(e);\n  void reloadUpsells(); // row may be gone; rebuild table from server truth\n  showError(e.message);\n}","handlingStrategy":"try-catch","validationCode":"const rowIsLive = (id: string, rows: Upsell[]) => rows.some((u) => u.id === id);\nif (!rowIsLive(id, upsells)) { void reloadUpsells(); return; }","typeGuard":"const isResponseError = (e: unknown): e is ResponseError => e instanceof ResponseError;","tryCatchPattern":"try {\n  await pauseUpsell(id);\n} catch (e) {\n  assertResponseError(e);\n  void reloadUpsells(); // row may be gone (404) — rebuild from server truth\n  showError(e.message);\n}","preventionTips":["Disable the pause/resume toggle while a toggle is in flight","Refresh the table before acting when multiple tabs are open","Check the POST status code to distinguish 404 (stale row) from 401 (session)"],"tags":["upsells","http","fetch","typescript"],"backgroundTag":"http-404-not-found","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}