{"record":{"id":"6dc4d90722ba7951","repo":"upstash/context7","slug":"fallback-http-response-status-from-respon","errorCode":null,"errorMessage":"${fallback} (HTTP ${response.status} from ${response.url}): ${excerpt}","messagePattern":"(.+?) \\(HTTP (.+?) from (.+?)\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/auth.ts","lineNumber":221,"sourceCode":"  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> {\n  // Hostname is shown on the server's verification page so the user can confirm\n  // that the device they're authorizing matches the one running the CLI\n  // (RFC 8628 §5.4 phishing resistance). Best-effort.\n  const params = new URLSearchParams({ client_id: clientId });\n  try {\n    const hostname = os.hostname();\n    if (hostname) params.set(\"hostname\", hostname);","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/upstash/context7/blob/ca15df0443ee770506fc4eb270d1efc71d483933/packages/cli/src/utils/auth.ts#L203-L239","documentation":"Thrown by oauthRequest() when postForm() succeeds but response.ok is false. describeErrorResponse() first tries to parse the body as an OAuth TokenErrorResponse (error/error_description); if that fails (e.g. HTML from an intercepting proxy) it falls back to '<fallback> (HTTP <status> from <url>): <200-char excerpt>'. The fallback string is supplied per call site (e.g. 'Failed to start device authorization').","triggerScenarios":"OAuth device-code/token endpoint returns 4xx with an OAuth error body (invalid_client, invalid_request, invalid_grant); an intermediary (proxy/gateway) returns its own HTML error page; the auth host returns a generic 500/502 JSON; the request reached the wrong virtual host.","commonSituations":"Wrong/migrated client_id; auth server behind a gateway that returns HTML for errors; base URL override pointing at the wrong service; misconfigured load balancer returning 502 with a non-JSON body; rate-limited by an edge proxy.","solutions":["Read the excerpt in the message — if it is HTML, the request is hitting a proxy/gateway, not the auth server; fix the base URL.","Confirm the OAuth client_id the CLI is using is still registered and not expired.","Reproduce with curl -i against the same URL to see the full status line and body.","If the body is a real OAuth error (invalid_grant etc.), address that specific error code rather than retrying blindly."],"exampleFix":"// before — fallback message only says \"Failed to start device authorization\"\n// after — log response.status + raw body excerpt before throwing for easier triage\nif (!response.ok) {\n  const detail = await describeErrorResponse(response, fallback);\n  console.error(`oauthRequest ${response.url} -> HTTP ${response.status}`);\n  throw new Error(detail);\n}","handlingStrategy":"try-catch","validationCode":"// Nothing to validate client-side beyond a well-formed URL + client_id;\n// surface that the request will go out.\nfunction isValidAuthUrl(u: string): boolean {\n  try {\n    const parsed = new URL(u);\n    return parsed.protocol === \"https:\" && Boolean(parsed.hostname);\n  } catch {\n    return false;\n  }\n}\nif (!isValidAuthUrl(baseUrl)) {\n  throw new Error(`Invalid auth base URL: ${baseUrl}`);\n}","typeGuard":"// Detect an HTML (non-OAuth) response body so the caller can tell proxy noise apart.\nfunction looksLikeHtmlExcerpt(msg: string): boolean {\n  return /\\b(?:<html|<!doctype|<body)/i.test(msg);\n}","tryCatchPattern":"try {\n  await oauthRequest(url, params, fallback);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (looksLikeHtmlExcerpt(msg)) {\n    throw new Error(`${fallback}: request hit a proxy/gateway HTML page, not the auth server. Check the base URL.`);\n  }\n  throw e; // otherwise it's a real OAuth error body — rethrow verbatim\n}","preventionTips":["Verify the auth base URL points at the real auth host, not a gateway that returns HTML errors.","Confirm client_id is current before initiating the flow; an invalid_client is the most common 4xx here.","Reproduce failing calls with curl -i to see whether the body is an OAuth error or intermediary noise."],"tags":["oauth","http","cli","error-body"],"backgroundTag":null,"analyzedSha":"ca15df0443ee770506fc4eb270d1efc71d483933","analyzedAt":"2026-08-12T13:31:48.440Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}