{"record":{"id":"062819572cacc109","repo":"upstash/context7","slug":"errorbody-error-errorbody-message-res-status","errorCode":null,"errorMessage":"errorBody.error || errorBody.message || res.statusText","messagePattern":"errorBody\\.error \\|\\| errorBody\\.message \\|\\| res\\.statusText","errorType":"exception","errorClass":"Context7Error","httpStatus":null,"severity":"error","filePath":"packages/sdk/src/http/index.ts","lineNumber":176,"sourceCode":"        if (requestOptions.signal?.aborted) {\n          throw error_;\n        }\n        error = error_ as Error;\n        if (i < this.retry.attempts) {\n          await new Promise((r) => setTimeout(r, this.retry.backoff(i)));\n        }\n      }\n    }\n    if (!res) {\n      throw error ?? new Error(\"Exhausted all retries\");\n    }\n\n    if (!res.ok) {\n      const errorBody = (await res.json().catch(() => ({}))) as {\n        error?: string;\n        message?: string;\n      };\n      throw new Context7Error(errorBody.error || errorBody.message || res.statusText);\n    }\n\n    const contentType = res.headers.get(\"content-type\");\n\n    if (contentType?.includes(\"application/json\")) {\n      const body = await res.json();\n      return { result: body as TResult };\n    } else {\n      const text = await res.text();\n      const headers = this.extractTxtResponseHeaders(res.headers);\n      return { result: text as TResult, headers };\n    }\n  }\n\n  private extractTxtResponseHeaders(headers: Headers): TxtResponseHeaders | undefined {\n    const page = headers.get(\"x-context7-page\");\n    const limit = headers.get(\"x-context7-limit\");\n    const totalPages = headers.get(\"x-context7-total-pages\");","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/upstash/context7/blob/5284672feb575908efead6fcf1b5e542f8d607bb/packages/sdk/src/http/index.ts#L158-L194","documentation":"After the HttpClient's retry loop is exhausted, any final non-ok response is converted to `Context7Error` with the message taken from the JSON body's `error` or `message` field, falling back to `res.statusText`. This is the single funnel for API-level failures: 401 invalid key, 404 unknown library, 429 rate limit, and hard 5xxs all surface here with whatever message the API supplied.","triggerScenarios":"Invalid or revoked API key (401); requesting a libraryId that does not exist (404); exceeding rate limits until retries are spent (429); context7 API 5xx persisting across retries.","commonSituations":"Rotated/expired ctx7sk key still in env; passing a library name instead of the resolved libraryId; bursty scripts hitting free-tier limits; API incident.","solutions":["Verify the key: it should start with `ctx7sk` and belong to an active account","For 404s, resolve the library via SearchLibraryCommand first and use the returned ID","Honor rate limits: back off (Retry-After / exponential) and cache results","Check status.context7.com or retry later for persistent 5xx"],"exampleFix":"// before\nconst docs = await client.getContext({ libraryName: 'react', topic: 'hooks' });\n\n// after: resolve the ID first, and handle Context7Error explicitly\ntry {\n  const libs = await client.searchLibrary({ query: 'react', libraryName: 'react' });\n  const docs = await client.getContext({ libraryId: libs[0]?.id, topic: 'hooks' });\n} catch (e) { if (e instanceof Context7Error) console.error(e.message); }","handlingStrategy":"retry","validationCode":"// Validate the key shape before first use\nconst key = process.env.CONTEXT7_API_KEY ?? '';\nif (!key.startsWith('ctx7sk')) console.warn('API key should start with ctx7sk');\n// Resolve library IDs before requesting context\nconst libs = await client.searchLibrary({ query, libraryName });\nconst libraryId = libs[0]?.id; // use resolved id to avoid 404s","typeGuard":"function isContext7Error(e: unknown): e is Context7Error {\n  return e instanceof Error && (e.constructor.name === 'Context7Error' || e.name === 'Context7Error');\n}","tryCatchPattern":"for (let attempt = 0; ; attempt++) {\n  try { return await client.getContext(params); }\n  catch (error) {\n    if (!isContext7Error(error)) throw error;\n    const msg = error.message;\n    if (/rate|429|too many/i.test(msg) && attempt < 3) { await sleep(2 ** attempt * 500); continue; }\n    if (/401|unauthorized|invalid.*key/i.test(msg)) throw new Error('check CONTEXT7_API_KEY');\n    throw error;\n  }\n}","preventionTips":["Cache resolved library IDs and documentation between runs","Back off on 429/5xx rather than hammering retries","Fail fast on key-shape problems: validate the ctx7sk prefix at startup"],"tags":["sdk","http","api","rate-limit","authentication"],"backgroundTag":"http-error-response","analyzedSha":"5284672feb575908efead6fcf1b5e542f8d607bb","analyzedAt":"2026-08-18T18:00:18.510Z","contentChangedAt":"2026-08-18T18:00:18.510Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}