{"record":{"id":"ff3fdae590facc3a","repo":"dubinc/dub","slug":"parseddata-as-apierror-error-message","errorCode":null,"errorMessage":"(parsedData as APIError).error.message","messagePattern":"\\(parsedData as APIError\\)\\.error\\.message","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/parser.ts","lineNumber":13,"sourceCode":"import type { APIError } from \"@/types\";\nimport type { Response as NodeFetchResponse } from \"node-fetch\";\n\ntype AnyResponse = Response | NodeFetchResponse;\n\nexport async function parseApiResponse<T>(response: AnyResponse): Promise<T> {\n  const contentType = response.headers.get(\"content-type\");\n\n  if (contentType?.includes(\"application/json\")) {\n    const parsedData = await response.json();\n\n    if (\"error\" in parsedData) {\n      throw new Error((parsedData as APIError).error.message);\n    }\n\n    return parsedData as T;\n  }\n\n  const textData = await response.text();\n\n  throw new Error(textData);\n}\n","sourceCodeStart":1,"sourceCodeEnd":23,"githubUrl":"https://github.com/dubinc/dub/blob/f216b94a24ca5a0a48c6543ee10392c9006c8b75/packages/cli/src/utils/parser.ts#L1-L23","documentation":"parseApiResponse inspects the response content-type; for JSON bodies containing an `error` key it throws an Error whose message is the API's error message from the Dub API error payload. This surfaces the real server-side reason (auth, validation, not found, rate limit) to the caller instead of returning a failed payload as data.","triggerScenarios":"Any call routed through parseApiResponse (`dub domains` / defaultDomains) where the Dub API returns a JSON body like {\"error\":{\"code\":\"bad_request\",\"message\":\"...\"}} with a 4xx/5xx status.","commonSituations":"Expired or wrong access token (401 unauthorized); invalid domain payload (422); referencing a nonexistent resource; hitting rate limits (429).","solutions":["Read the thrown message — it is the API's own message — and fix the request accordingly (token, domain name, params).","Re-authenticate with `dub login` if the message indicates unauthorized/invalid token.","Validate request payloads (domain format, required fields) before sending.","Check api.dub.co status / retry with backoff if the message indicates rate limiting or server error."],"exampleFix":"// before\nconst res = await createDomain({ slug: 'my domain!' });\n// after (validate before calling)\nif (!/^[a-z0-9.-]+$/.test(slug)) throw new Error('invalid domain slug');\nconst res = await createDomain({ slug });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isApiError(data: unknown): data is { error: { code: string; message: string } } {\n  return typeof data === \"object\" && data !== null && \"error\" in data && typeof (data as any).error?.message === \"string\";\n}","tryCatchPattern":"try {\n  const domains = await parseApiResponse(response);\n} catch (e) {\n  const msg = (e as Error).message;\n  if (/unauthorized|invalid token/i.test(msg)) {\n    await relogin();\n  } else if (/rate.?limit/i.test(msg)) {\n    await sleep(backoff);\n  } else {\n    console.error(\"Dub API error:\", msg);\n  }\n}","preventionTips":["Validate payloads before sending (domain format, required fields).","Refresh access tokens proactively rather than after 401s.","Respect rate limits with client-side throttling.","Log the thrown message verbatim — it is the API's own diagnostic."],"tags":["api","http-error","json"],"backgroundTag":"api-error-response","analyzedSha":"f216b94a24ca5a0a48c6543ee10392c9006c8b75","analyzedAt":"2026-08-31T18:35:50.395Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}