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

  1. Verify the key: it should start with `ctx7sk` and belong to an active account
  2. For 404s, resolve the library via SearchLibraryCommand first and use the returned ID
  3. Honor rate limits: back off (Retry-After / exponential) and cache results
  4. 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

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


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)