{"record":{"id":"c440e56b7148f2f1","repo":"windmill-labs/windmill","slug":"got-an-html-response-from-url-status-status","errorCode":null,"errorMessage":"Got an HTML response from ${url} (status ${status}${cfPart ? `, ${cfPart}` : \"\"}). The request was intercepted by an upstream auth gateway (likely Cloudflare Access) before reaching Windmill. Verify the runner is on the right network or pass service-token headers via the HEADERS env var (e.g. HEADERS=\"CF-Access-Client-Id: <id>, CF-Access-Client-Secret: <secret>\"). Body starts with: ${JSON.stringify(bodySnippet.slice(0, 120))}","messagePattern":"Got an HTML response from (.+?) \\(status (.+?)(.+?)` : \"\"\\}\\)\\. The request was intercepted by an upstream auth gateway \\(likely Cloudflare Access\\) before reaching Windmill\\. Verify the runner is on the right network or pass service-token headers via the HEADERS env var \\(e\\.g\\. HEADERS=\"CF-Access-Client-Id: <id>, CF-Access-Client-Secret: <secret>\"\\)\\. Body starts with: (.+?)","errorType":"http","errorClass":"AuthGatewayChallengeError","httpStatus":null,"severity":"error","filePath":"cli/src/utils/http_guards.ts","lineNumber":59,"sourceCode":"\n  // Cheap check first; only peek the body when something already smells off.\n  if (!looksHtml && cfMitigated !== \"challenge\") return;\n\n  let snippet = \"\";\n  try {\n    snippet = (await response.clone().text()).slice(0, 256);\n  } catch {\n    /* body unreadable — fall through */\n  }\n\n  const isChallenge =\n    cfMitigated === \"challenge\" ||\n    ACCESS_TITLE.test(snippet) ||\n    (looksHtml && HTML_DOCTYPE.test(snippet));\n\n  if (!isChallenge) return;\n\n  throw new AuthGatewayChallengeError(\n    url || response.url || \"(unknown)\",\n    response.headers.get(\"cf-ray\") ?? undefined,\n    cfMitigated,\n    response.status,\n    snippet,\n  );\n}\n","sourceCodeStart":41,"sourceCodeEnd":67,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/utils/http_guards.ts#L41-L67","documentation":"detectAuthGatewayChallenge in cli/src/utils/http_guards.ts throws AuthGatewayChallengeError when an HTTP response from the Windmill instance looks like HTML served by an upstream auth gateway (Cloudflare Access, SSO wall) instead of the expected JSON API response. The CLI checks the content-type, the cf-mitigated header, and a 256-byte body snippet for a Cloudflare Access sign-in page or any HTML doctype, then aborts so the HTML body is never misparsed as a typed Windmill response. The error carries url, cf-ray, cf-mitigated, status, and a body snippet for diagnosis.","triggerScenarios":"Any CLI API call routed through detectAuthGatewayChallenge (generateInlineScriptLock, streamJobWithSSE, docs, updateFlow, createScript, preview) against a Windmill instance behind Cloudflare Access when the request lacks valid service-token headers: response content-type is text/html and body starts with <!doctype or <html, or cf-mitigated: challenge header present, or the body matches the 'Sign in ... Cloudflare Access' title.","commonSituations":"Running wmill sync/flow dev against a Cloudflare-Access-protected instance from a machine/network outside the allowlist; a corporate proxy or WAF intercepting requests; HEADERS env var missing or malformed (no CF-Access-Client-Id/Secret); pointing the CLI at the public domain instead of an internal address; token expired so Access falls back to the browser login page.","solutions":["Set service-token headers via the env var, e.g. HEADERS=\"CF-Access-Client-Id: <id>, CF-Access-Client-Secret: <secret>\" before running the CLI","Run the CLI from a network position that reaches Windmill directly (VPN, allowlisted runner) bypassing the Access challenge","Point BASE_URL/remote at an internal URL that is not behind the auth gateway","Inspect the cf-ray/cf-mitigated values in the error and check your Cloudflare Access logs for the rejected request","If behind a custom gateway, ensure it forwards requests rather than serving its HTML login page to non-browser clients"],"exampleFix":"// before: CLI run without Access credentials\nexport BASE_URL=https://windmill.example.com\nwmill sync push\n// AuthGatewayChallengeError: Got an HTML response ...\n\n// after: supply Cloudflare Access service tokens\nexport BASE_URL=https://windmill.example.com\nexport HEADERS=\"CF-Access-Client-Id: myapp.access, CF-Access-Client-Secret: ********\"\nwmill sync push","handlingStrategy":"try-catch","validationCode":"const ct = (await fetch(url, { method: \"HEAD\" })).headers.get(\"content-type\") ?? \"\";\nif (ct.includes(\"text/html\")) {\n  throw new Error(`${url} serves HTML — an auth gateway is intercepting; configure HEADERS service tokens before using the CLI`);\n}","typeGuard":"function isAuthGatewayChallenge(err: unknown): err is import(\"./http_guards.ts\").AuthGatewayChallengeError {\n  return err instanceof Error && (err as any).name === \"AuthGatewayChallengeError\" && typeof (err as any).cfRay === \"object\";\n}","tryCatchPattern":"import { AuthGatewayChallengeError } from \"./utils/http_guards.ts\";\ntry {\n  await wmill.createScript(payload);\n} catch (e) {\n  if (e instanceof AuthGatewayChallengeError) {\n    console.error(`Gateway at ${e.url} (status ${e.status}, cf-ray=${e.cfRay}) blocked the request. Set HEADERS with CF-Access service tokens.`);\n    process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Always set HEADERS with CF-Access-Client-Id/Secret when the instance sits behind Cloudflare Access","Run CI/CLI workloads from network positions allowlisted to bypass the Access challenge","Smoke-test with a cheap call (e.g. `wmill workspace list`) and assert the response is JSON before long operations","Watch for cf-mitigated: challenge response headers in any custom HTTP plumbing","Prefer internal service URLs over public domains for machine traffic"],"tags":["network","http","cloudflare","auth","cli"],"backgroundTag":"cloudflare-access-challenge","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}