upstash/context7 · error · Context7Error

authentication_error

authentication_error

Error message

The auth token provider returned an empty token

What it means

headersForToken builds request headers by resolving the auth token from a token provider. If a static authToken was configured but the provider dynamically returned an empty/undefined token, the client throws a non-retryable Context7Error with code authentication_error, because silently sending an unauthenticated request would fail downstream anyway.

Solutions

  1. Make the token provider return a valid non-empty string or fail loudly before requests start
  2. If the token is dynamic, ensure the provider awaits token refresh and throws a meaningful error when the token cannot be obtained
  3. Do not configure a static authToken if you cannot guarantee a token; omit it and rely on apiKey authentication
  4. Check provider ordering: ensure authentication/login completes before the client issues requests

Example fix

// before
const client = new Context7Client({ authToken: () => store.token ?? '' });
// after
const client = new Context7Client({
  authToken: () => {
    const t = store.token;
    if (!t) throw new Error('Not authenticated yet');
    return t;
  },
});
Defensive patterns

Strategy: try-catch

Validate before calling

const token = typeof authToken === 'function' ? authToken() : authToken;
if (config.authToken !== undefined && !token) throw new Error('auth token provider returned empty token');

Type guard

function hasToken(t: string | undefined | (() => string | undefined)): t is string {
  const v = typeof t === 'function' ? t() : t;
  return typeof v === 'string' && v.length > 0;
}

Try / catch

try {
  const data = await client.exec(cmd);
} catch (e) {
  if (e instanceof Context7Error && e.code === 'authentication_error' && !e.retryable) {
    // re-authenticate / refresh token, then rebuild the client
  } else { throw e; }
}

Prevention

When it happens

Trigger: Configuring authToken as a function () => string whose return value is '' or undefined at request time (e.g. reading a token from a store that is not yet populated, an expired/cleared session, or an async provider whose value was not awaited).

Common situations: Token read from storage/cache before login completes; token refresh job failing and leaving an empty string; passing a sync function wrapping an async token fetch (returning undefined); race where the provider is called before credentials load.

Related errors


AI-assisted analysis of upstash/context7@4416fb855b (2026-09-16). Data as JSON: /api/errors/7f5e9c93180c9b25. Report an issue: GitHub.

Appendix: source

Thrown at packages/sdk/src/http/index.ts:166

      const metadata = extractResponseMetadata(response, attempt);
      this.onResponse?.(metadata);
      const shouldRetry =
        canRetry && this.retry.statuses.has(response.status) && attempt < this.retry.retries;
      if (!shouldRetry) return { response, metadata };

      await response.body?.cancel().catch(() => undefined);
      await wait(
        retryDelay(this.retry.backoff(attempt), metadata.rateLimit?.retryAfter),
        abortState.signal
      );
    }

    throw new Error("Unreachable retry state");
  }

  private headersForToken(authToken: string | undefined): Record<string, string> {
    if (this.authToken !== undefined && !authToken) {
      throw new Context7Error("The auth token provider returned an empty token", {
        code: "authentication_error",
        retryable: false,
      });
    }

    return authToken ? { ...this.headers, Authorization: `Bearer ${authToken}` } : this.headers;
  }
}

function buildUrl(baseUrl: string, method: "GET" | "POST", request: Context7Request): string {
  const url = [baseUrl, ...(request.path ?? [])].join("/");
  if (method !== "GET" || !request.query) return url;

  const query = new URLSearchParams();
  for (const [key, value] of Object.entries(request.query)) {
    if (value !== undefined) query.append(key, String(value));
  }
  const queryString = query.toString();

View on GitHub (pinned to 4416fb855b)