upstash/context7 · error · Context7Error
errorBody.error || errorBody.message || res.statusText
Error message
errorBody.error || errorBody.message || res.statusText
What it means
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.
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
Example fix
// before
const docs = await client.getContext({ libraryName: 'react', topic: 'hooks' });
// after: resolve the ID first, and handle Context7Error explicitly
try {
const libs = await client.searchLibrary({ query: 'react', libraryName: 'react' });
const docs = await client.getContext({ libraryId: libs[0]?.id, topic: 'hooks' });
} catch (e) { if (e instanceof Context7Error) console.error(e.message); } Defensive patterns
Strategy: retry
Validate before calling
// Validate the key shape before first use
const key = process.env.CONTEXT7_API_KEY ?? '';
if (!key.startsWith('ctx7sk')) console.warn('API key should start with ctx7sk');
// Resolve library IDs before requesting context
const libs = await client.searchLibrary({ query, libraryName });
const libraryId = libs[0]?.id; // use resolved id to avoid 404s Type guard
function isContext7Error(e: unknown): e is Context7Error {
return e instanceof Error && (e.constructor.name === 'Context7Error' || e.name === 'Context7Error');
} Try / catch
for (let attempt = 0; ; attempt++) {
try { return await client.getContext(params); }
catch (error) {
if (!isContext7Error(error)) throw error;
const msg = error.message;
if (/rate|429|too many/i.test(msg) && attempt < 3) { await sleep(2 ** attempt * 500); continue; }
if (/401|unauthorized|invalid.*key/i.test(msg)) throw new Error('check CONTEXT7_API_KEY');
throw error;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Rotated/expired ctx7sk key still in env; passing a library name instead of the resolved libraryId; bursty scripts hitting free-tier limits; API incident.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- API key is required. Pass it in the config or set…
- authentication_error
- Failed to fetch user info
- ( )
- HTTP from /api/auth/mcp
AI-assisted analysis of upstash/context7@5284672feb (2026-08-18).
Data as JSON: /api/errors/062819572cacc109.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/src/http/index.ts:176
if (requestOptions.signal?.aborted) {
throw error_;
}
error = error_ as Error;
if (i < this.retry.attempts) {
await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
}
}
}
if (!res) {
throw error ?? new Error("Exhausted all retries");
}
if (!res.ok) {
const errorBody = (await res.json().catch(() => ({}))) as {
error?: string;
message?: string;
};
throw new Context7Error(errorBody.error || errorBody.message || res.statusText);
}
const contentType = res.headers.get("content-type");
if (contentType?.includes("application/json")) {
const body = await res.json();
return { result: body as TResult };
} else {
const text = await res.text();
const headers = this.extractTxtResponseHeaders(res.headers);
return { result: text as TResult, headers };
}
}
private extractTxtResponseHeaders(headers: Headers): TxtResponseHeaders | undefined {
const page = headers.get("x-context7-page");
const limit = headers.get("x-context7-limit");
const totalPages = headers.get("x-context7-total-pages");View on GitHub (pinned to 5284672feb)