upstash/context7 · error · Error

describeConnectionError(error, url)

Error message

describeConnectionError(error, url)

What it means

`postForm` wraps any `fetch` rejection during OAuth calls into a single descriptive Error built by `describeConnectionError`: `Could not reach {url}: {detail} ({code})` plus a remediation hint selected from the underlying cause code — TLS hints for certificate errors, DNS_HINT for ENOTFOUND/EAI_AGAIN, BLOCKED_HINT for ECONNREFUSED/ECONNRESET/EHOSTUNREACH, TIMEOUT_HINT for ETIMEDOUT/UND_ERR_CONNECT_TIMEOUT. The original network error is swallowed; only this message survives.

Source

Thrown at packages/cli/src/utils/auth.ts:214

}

function describeConnectionError(error: unknown, url: string): string {
  const { code, message } = getErrorCause(error);
  const detail = message || (error instanceof Error ? error.message : String(error));
  const hint = (code && CONNECTION_HINTS[code]) || DEFAULT_HINT;

  return `Could not reach ${url}: ${detail}${code ? ` (${code})` : ""}\n${hint}`;
}

async function postForm(url: string, params: URLSearchParams): Promise<Response> {
  try {
    return await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: params.toString(),
    });
  } catch (error) {
    throw new Error(describeConnectionError(error, url));
  }
}

async function oauthRequest<T>(url: string, params: URLSearchParams, fallback: string): Promise<T> {
  const response = await postForm(url, params);
  if (!response.ok) {
    throw new Error(await describeErrorResponse(response, fallback));
  }
  return (await response.json()) as T;
}

/** RFC 8628 §3.2 default poll interval when the server omits `interval`. */
export const DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;

export async function startDeviceAuthorization(
  baseUrl: string,
  clientId: string
): Promise<DeviceAuthorizationResponse> {

View on GitHub (pinned to 5284672feb)

Solutions

  1. For TLS errors, point NODE_EXTRA_CA_CERTS at your organization's root CA certificate
  2. For DNS errors (ENOTFOUND/EAI_AGAIN), fix resolver/VPN or wait for network
  3. Node fetch ignores HTTPS_PROXY — configure an undici ProxyAgent dispatcher if you must go through a proxy
  4. Verify basic reachability: curl -v <baseUrl>/ping from the same environment

Example fix

# before: behind corporate proxy, fetch fails opaquely
context7 login

# after: trust the proxy's root CA and route via undici
export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root.pem
export HTTPS_PROXY=http://proxy.corp:3128  # needs an undici ProxyAgent in code
Defensive patterns

Strategy: retry

Validate before calling

// Preflight: DNS + TCP reachability before starting OAuth
import { promises as dns } from 'dns';
async function hostReachable(url: string): Promise<boolean> {
  try { await dns.lookup(new URL(url).hostname); return true; } catch { return false; }
}

Try / catch

try {
  await postForm(url, params);
} catch (error) {
  const msg = error instanceof Error ? error.message : String(error);
  if (/ENOTFOUND|EAI_AGAIN/.test(msg)) throw new Error('DNS failure — check VPN/resolver');
  if (/ETIMEDOUT|UND_ERR_CONNECT_TIMEOUT/.test(msg)) await sleep(2000); // retry once
  else throw error;
}

Prevention

When it happens

Trigger: Any `context7 login` / token refresh where DNS fails (ENOTFOUND, EAI_AGAIN), a TLS-intercepting proxy breaks cert verification (UNABLE_TO_VERIFY_LEAF_SIGNATURE, SELF_SIGNED_CERT_IN_CHAIN), a firewall resets the connection (ECONNRESET, ECONNREFUSED), or the connect times out (UND_ERR_CONNECT_TIMEOUT, ETIMEDOUT).

Common situations: Corporate MITM proxy without NODE_EXTRA_CA_CERTS set to the org root CA; VPN split-tunnel DNS; Node's fetch silently ignoring HTTPS_PROXY (it does not use it automatically); captive portal; offline machine.

Related errors


AI-assisted analysis of upstash/context7@5284672feb (2026-08-18). Data as JSON: /api/errors/be7968bc4b378faa. Report an issue: GitHub.