{"record":{"id":"6397a95aa8bfbbf9","repo":"antiwork/gumroad","slug":"responsedata-error","errorCode":null,"errorMessage":"${responseData.error}","messagePattern":"\\$\\{responseData\\.error\\}","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/data/global_affiliates.ts","lineNumber":22,"sourceCode":"\nexport type Product = {\n  recommendable: boolean;\n  name: string;\n  short_url: string;\n  formatted_price: string;\n};\n\nexport const searchGlobalAffiliatesProductEligibility = async ({ query }: { query: string }): Promise<Product> => {\n  const response = await request({\n    method: \"GET\",\n    accept: \"json\",\n    url: Routes.global_affiliates_product_eligibility_path(query),\n  });\n  if (!response.ok) throw new ResponseError();\n  const responseData = typia.assert<{ success: true; product: Product } | { success: false; error: string }>(\n    await response.json(),\n  );\n  if (!responseData.success) throw new ResponseError(responseData.error);\n  return responseData.product;\n};\n","sourceCodeStart":4,"sourceCodeEnd":25,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/global_affiliates.ts#L4-L25","documentation":"The eligibility endpoint answered 200 OK with { success: false, error: string }: the server processed the lookup but the product failed the global-affiliates eligibility rules. The thrown ResponseError carries the server's own error string, so e.message is the actionable text — unlike the sibling generic throw at line 18 which covers HTTP-level failures.","triggerScenarios":"The queried product exists but is not eligible for the affiliates program: recommendable is false, the product type cannot be affiliated, it is unpublished/draft, or its creator has opted out. The controller renders { success: false, error } with HTTP 200.","commonSituations":"Testing the lookup with a membership/bundle the program does not support; pasting a link to your own product when self-referrals are excluded; the product was unpublished after the link was shared.","solutions":["Read e.message — it is the server's eligibility reason and should be shown verbatim to the user.","Confirm the product is published and affiliate-eligible in its settings.","If the message seems wrong, replay the GET from the Network tab and inspect the JSON error field.","Do not retry the identical query — this is a deterministic domain rejection, not a transient failure."],"exampleFix":"// caller\try {\n  const product = await searchGlobalAffiliatesProductEligibility({ query });\n} catch (e) {\n  assertResponseError(e);\n  showError(e.message); // server's own eligibility reason, e.g. \"This product isn't eligible\"\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isEligibilityRejection = (json: unknown): json is { success: false; error: string } =>\n  typeof json === 'object' && json !== null && (json as { success?: unknown }).success === false && typeof (json as { error?: unknown }).error === 'string';","tryCatchPattern":"try {\n  const product = await searchGlobalAffiliatesProductEligibility({ query });\n} catch (e) {\n  assertResponseError(e);\n  showError(e.message); // the server's eligibility reason — display it verbatim\n}","preventionTips":["Treat success:false as a normal outcome of a user-driven lookup, not a defect.","Show e.message rather than a canned string; the server wording is the diagnosis.","Don't gate the call on client-side guesses about eligibility — the server owns the rules."],"tags":["affiliates","domain-error","api-contract","typia"],"backgroundTag":"api-error-response-body","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}