{"record":{"id":"be7968bc4b378faa","repo":"upstash/context7","slug":"describeconnectionerror-error-url","errorCode":null,"errorMessage":"describeConnectionError(error, url)","messagePattern":"describeConnectionError\\(error, url\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/auth.ts","lineNumber":214,"sourceCode":"}\n\nfunction describeConnectionError(error: unknown, url: string): string {\n  const { code, message } = getErrorCause(error);\n  const detail = message || (error instanceof Error ? error.message : String(error));\n  const hint = (code && CONNECTION_HINTS[code]) || DEFAULT_HINT;\n\n  return `Could not reach ${url}: ${detail}${code ? ` (${code})` : \"\"}\\n${hint}`;\n}\n\nasync function postForm(url: string, params: URLSearchParams): Promise<Response> {\n  try {\n    return await fetch(url, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n      body: params.toString(),\n    });\n  } catch (error) {\n    throw new Error(describeConnectionError(error, url));\n  }\n}\n\nasync function oauthRequest<T>(url: string, params: URLSearchParams, fallback: string): Promise<T> {\n  const response = await postForm(url, params);\n  if (!response.ok) {\n    throw new Error(await describeErrorResponse(response, fallback));\n  }\n  return (await response.json()) as T;\n}\n\n/** RFC 8628 §3.2 default poll interval when the server omits `interval`. */\nexport const DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;\n\nexport async function startDeviceAuthorization(\n  baseUrl: string,\n  clientId: string\n): Promise<DeviceAuthorizationResponse> {","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/upstash/context7/blob/5284672feb575908efead6fcf1b5e542f8d607bb/packages/cli/src/utils/auth.ts#L196-L232","documentation":"`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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["For TLS errors, point NODE_EXTRA_CA_CERTS at your organization's root CA certificate","For DNS errors (ENOTFOUND/EAI_AGAIN), fix resolver/VPN or wait for network","Node fetch ignores HTTPS_PROXY — configure an undici ProxyAgent dispatcher if you must go through a proxy","Verify basic reachability: curl -v <baseUrl>/ping from the same environment"],"exampleFix":"# before: behind corporate proxy, fetch fails opaquely\ncontext7 login\n\n# after: trust the proxy's root CA and route via undici\nexport NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root.pem\nexport HTTPS_PROXY=http://proxy.corp:3128  # needs an undici ProxyAgent in code","handlingStrategy":"retry","validationCode":"// Preflight: DNS + TCP reachability before starting OAuth\nimport { promises as dns } from 'dns';\nasync function hostReachable(url: string): Promise<boolean> {\n  try { await dns.lookup(new URL(url).hostname); return true; } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await postForm(url, params);\n} catch (error) {\n  const msg = error instanceof Error ? error.message : String(error);\n  if (/ENOTFOUND|EAI_AGAIN/.test(msg)) throw new Error('DNS failure — check VPN/resolver');\n  if (/ETIMEDOUT|UND_ERR_CONNECT_TIMEOUT/.test(msg)) await sleep(2000); // retry once\n  else throw error;\n}","preventionTips":["Export NODE_EXTRA_CA_CERTS in corporate proxy environments before any login","Node fetch ignores HTTPS_PROXY — configure an undici ProxyAgent dispatcher explicitly","Run a connectivity check to the API host before starting interactive OAuth flows"],"tags":["network","oauth","proxy","tls","dns","node-fetch"],"backgroundTag":"connection-refused","analyzedSha":"5284672feb575908efead6fcf1b5e542f8d607bb","analyzedAt":"2026-08-18T18:00:18.510Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}