{"record":{"id":"43db61b5ad6fe954","repo":"antiwork/gumroad","slug":"something-went-wrong-43db61","errorCode":null,"errorMessage":"Something went wrong.","messagePattern":"Something went wrong\\.","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/data/customers.ts","lineNumber":212,"sourceCode":"    response,\n    cancel: () => abort.abort(),\n  };\n};\n\nexport type MissedPost = {\n  id: string;\n  name: string;\n  url: string;\n  published_at: string;\n};\nexport const getMissedPosts = (purchaseId: string, purchaseEmail: string) =>\n  request({\n    method: \"GET\",\n    accept: \"json\",\n    url: Routes.missed_posts_path(purchaseId, { purchase_email: purchaseEmail }),\n  })\n    .then((res) => {\n      if (!res.ok) throw new ResponseError();\n      return res.json();\n    })\n    .then((json) => typia.assert<MissedPost[]>(json));\n\nexport type CustomerEmail = { id: string; name: string; state: string; state_at: string } & (\n  | { type: \"receipt\"; url: string }\n  | { type: \"post\" }\n);\nexport const getCustomerEmails = (purchaseId: string) =>\n  request({\n    method: \"GET\",\n    accept: \"json\",\n    url: Routes.customer_emails_path(purchaseId),\n  })\n    .then((res) => {\n      if (!res.ok) throw new ResponseError();\n      return res.json();\n    })","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/customers.ts#L194-L230","documentation":"ResponseError (\"Something went wrong.\") thrown by getMissedPosts when GET /customers/missed_posts/:purchase_id?purchase_email=... answers non-2xx. This feeds the buyer Library's \"missed posts\" list, and the purchase_email parameter doubles as the access check. Since request() pre-handles 5xx/429/network errors, this throw means a 4xx — typically 404 when the purchase does not exist or the email does not match it, or 401 for session problems.","triggerScenarios":"Passing a purchase_email that differs from the purchase's actual email (typo, different casing/alias, or the purchase was made with another address); a purchase_id for a deleted or fully refunded purchase; reader session expired on the library page; ids hand-copied from an old link.","commonSituations":"Buyers forwarding library links between their own two email addresses; purchases made via PayPal with a different email than the Gumroad login; test purchases later deleted; library tabs left open across account switches.","solutions":["Confirm the purchase_email passed in matches the purchase's email exactly, including spelling — it is the access credential for this endpoint.","DevTools → Network: a 404 means purchase-not-found-or-email-mismatch; verify the purchase exists under that email in the library.","401: re-authenticate (reload to login) and retry.","Handle the failure by hiding the missed-posts section rather than breaking the whole library page."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nif (!purchaseId || !EMAIL.test(purchaseEmail))\n  throw new Error(\"A purchase and its email are required to list missed posts.\");\nawait getMissedPosts(purchaseId, purchaseEmail);","typeGuard":"import { ResponseError } from \"$app/utils/request\";\nconst isResponseError = (e: unknown): e is ResponseError => e instanceof ResponseError;","tryCatchPattern":"import { assertResponseError } from \"$app/utils/request\";\ntry {\n  const posts = await getMissedPosts(purchaseId, purchaseEmail);\n} catch (e) {\n  assertResponseError(e);\n  setMissedPosts([]); // hide the section instead of failing the library page\n}","preventionTips":["The purchase_email is the access credential — always pass the exact email tied to the purchase.","Bind purchaseId/purchaseEmail from the purchase's own record, never from URL parameters a user can edit.","Degrade to an empty missed-posts section; a single bad purchase should not break the Library."],"tags":["library","missed-posts","purchase-email","buyer","http","fetch"],"backgroundTag":"http-4xx-response","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}