openclaw - ✅(Solved) Fix [Bug] openai-codex provider blocked by Cloudflare JS Challenge when accessed via proxy in mainland China [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#67670Fetched 2026-04-17 08:29:49
View on GitHub
Comments
0
Participants
1
Timeline
14
Reactions
0
Author
Participants
Timeline (top)
referenced ×13cross-referenced ×1

Error Message

Error Messages Observed

  1. LLM request failed: network connection error. — timeout variant
  • Related issue: #64092 (error classification for 403)
  • Related PR: #67642 (classify Cloudflare HTML error pages)

Root Cause

Cloudflare detects Node.js native fetch TLS fingerprint (JA3/JA4) as non-browser traffic and triggers a JS Challenge. This happens regardless of:

  • Proxy node location (tested Japan 94.177.131.104, Canada 23.132.28.49, multiple IPs)
  • Request headers (even with full browser User-Agent and Sec-CH-UA headers)
  • OAuth token validity (token valid with 200+ hours remaining)
  • Transport mode (both HTTP/SSE and WebSocket get 403)

Key evidence: Using Python cloudscraper library (which mimics Chrome TLS fingerprint) with the same proxy and same OAuth token returns 200 OK successfully. This confirms the issue is purely TLS fingerprint-based detection.

Fix Action

Workaround

Deploy a local Python reverse proxy using cloudscraper to bypass Cloudflare TLS fingerprint detection:

  1. Install cloudscraper:

    python3 -m venv /tmp/cftest && /tmp/cftest/bin/pip install cloudscraper
  2. Run a local HTTP reverse proxy (port 18792) that forwards requests to chatgpt.com/backend-api via cloudscraper with Chrome TLS fingerprint emulation.

  3. Configure OpenClaw to route openai-codex through the local proxy:

    {
      "models": {
        "providers": {
          "openai-codex": {
            "baseUrl": "http://127.0.0.1:18792",
            "models": [],
            "request": {
              "allowPrivateNetwork": true
            }
          }
        }
      }
    }
  4. Restart gateway: openclaw gateway restart

PR fix notes

PR #67717: fix(agents): detect Cloudflare challenge pages and provide actionable error message

Description (problem / solution / changelog)

Summary

  • Problem: Cloudflare returns 403 with JS Challenge page when openai-codex provider accessed via proxy from mainland China. Current error message says "Authentication failed" which is misleading — the issue is TLS fingerprint detection, not auth.
  • Why it matters: Users waste time re-authenticating when the real fix is a reverse proxy with browser-like TLS fingerprint.
  • What changed: Added cloudflare_challenge failure kind to detect Cloudflare challenge pages (403 with cf-browser-verification, challenges.cloudflare.com, etc.) and return actionable error message explaining TLS fingerprinting + reverse proxy workaround.
  • What did NOT change: Generic 403 HTML errors still classified as auth_html_403. No changes to transport layer, provider config, or TLS implementation.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #67670
  • Related #67642 (Cloudflare HTML error classification for 5xx)
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: classifyProviderRuntimeFailureKind classifies all 403 HTML responses as auth_html_403 without checking for Cloudflare-specific challenge patterns. Cloudflare challenge pages contain distinctive markers (cf-browser-verification, challenges.cloudflare.com) that distinguish them from genuine auth failures.
  • Missing detection / guardrail: No pattern matching for Cloudflare challenge page signatures before falling through to generic 403 HTML classification.
  • Contributing context: Issue #67642 fixed Cloudflare 5xx HTML classification but didn't address 403-specific challenge pages.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/agents/pi-embedded-helpers/provider-error-patterns.test.ts
  • Scenario the test should lock in: 403 HTML with Cloudflare challenge patterns (cf-browser-verification, challenges.cloudflare.com, _cf_chl_, just a moment, ray id:) returns cloudflare_challenge failure kind, not auth_html_403.
  • Why this is the smallest reliable guardrail: Unit test directly verifies classification logic with minimal HTML fixture containing only Cloudflare challenge markers.
  • Existing test that already covers this: None (new functionality).
  • If no new test is added, why not: N/A (3 new tests added).

User-visible / Behavior Changes

Before: Users seeing Cloudflare 403 challenges get misleading error:

"Authentication failed with an HTML 403 response from the provider. Re-authenticate and verify your provider account access."

After: Users get actionable error explaining root cause and workaround:

"Cloudflare blocked the request with a browser challenge (HTTP 403). Node.js TLS fingerprint was detected as non-browser traffic. Set up a local reverse proxy using a browser-compatible HTTP client (e.g. Python cloudscraper) and point the provider's baseUrl to it."

Diagram (if applicable)

Before:
403 + Cloudflare challenge HTML → classifyProviderRuntimeFailureKind
  → isHtmlErrorResponse(403) → "auth_html_403"
  → "Authentication failed" (misleading)

After:
403 + Cloudflare challenge HTML → classifyProviderRuntimeFailureKind
  → isCloudflareChallengePage(403) → "cloudflare_challenge"
  → "Cloudflare blocked... TLS fingerprint... reverse proxy" (actionable)

403 + generic HTML → classifyProviderRuntimeFailureKind
  → isHtmlErrorResponse(403) → "auth_html_403" (unchanged)

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: macOS 26.3 (arm64), Windows 11 Pro
  • Runtime/container: Node.js v22.22.2
  • Model/provider: openai-codex (chatgpt.com/backend-api)
  • Integration/channel: N/A
  • Relevant config: Proxy routing traffic through VPN/Quantumult X from mainland China

Steps

  1. Configure openai-codex provider with valid OAuth token
  2. Route traffic through proxy from mainland China (or any region where Cloudflare triggers JS challenges for Node.js TLS fingerprints)
  3. Attempt LLM request via openai-codex provider
  4. Observe 403 response with Cloudflare challenge HTML

Expected

Error message explains TLS fingerprint detection and suggests reverse proxy workaround.

Actual

Before fix: "Authentication failed with an HTML 403 response from the provider." After fix: "Cloudflare blocked the request with a browser challenge (HTTP 403). Node.js TLS fingerprint was detected as non-browser traffic. Set up a local reverse proxy using a browser-compatible HTTP client (e.g. Python cloudscraper) and point the provider's baseUrl to it."

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Test results:

  • 3 new tests added, all passing (39/39 total in test suite)
  • Test: "classifies 403 Cloudflare challenge as cloudflare_challenge" ✅
  • Test: "classifies non-Cloudflare 403 HTML as auth_html_403 (regression guard)" ✅
  • Test: "formats Cloudflare challenge error with TLS fingerprint guidance" ✅

Human Verification (required)

Verified scenarios:

  • Cloudflare challenge HTML (with cf-browser-verification, challenges.cloudflare.com, _cf_chl_, just a moment, ray id:) → cloudflare_challenge classification
  • Generic 403 HTML (no Cloudflare patterns) → auth_html_403 classification (regression guard)
  • Error message contains "Cloudflare blocked", "TLS fingerprint", "reverse proxy", "cloudscraper"

Edge cases checked:

  • 403 HTML without Cloudflare patterns still returns auth_html_403
  • Non-403 status codes with Cloudflare patterns do not trigger cloudflare_challenge (status check first)
  • HTML responses without cf-browser-verification or other markers fall through to generic classification

What I did NOT verify:

  • Actual Cloudflare challenge page in production (requires mainland China proxy setup)
  • Integration with live openai-codex provider endpoint

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: False positives — generic HTML pages containing "cloudflare" text might get misclassified as challenges.

    • Mitigation: Regex requires multiple CF-specific patterns (cf-browser-verification, challenges.cloudflare.com, etc.), not just keyword "cloudflare". Pattern is case-insensitive and matches partial strings safely.
  • Risk: Cloudflare changes challenge page structure, patterns no longer match.

    • Mitigation: Regex covers multiple stable markers (div ID, script domain, form field prefix, heading text, Ray ID footer). Unlikely all change simultaneously. If needed, patterns can be updated in future PR.

Changed files

  • .gitignore (modified, +1/-0)
  • src/agents/pi-embedded-helpers/errors.ts (modified, +25/-0)
  • src/agents/pi-embedded-helpers/provider-error-patterns.test.ts (modified, +58/-0)

Code Example

python3 -m venv /tmp/cftest && /tmp/cftest/bin/pip install cloudscraper

---

{
     "models": {
       "providers": {
         "openai-codex": {
           "baseUrl": "http://127.0.0.1:18792",
           "models": [],
           "request": {
             "allowPrivateNetwork": true
           }
         }
       }
     }
   }
RAW_BUFFERClick to expand / collapse

Environment

  • OpenClaw version: 2026.4.14
  • OS: macOS 26.3 (arm64), Mac mini
  • Node.js: v22.22.2
  • Network: Mainland China, traffic routed through VPN/proxy (Quantumult X)

Problem

The openai-codex provider (chatgpt.com/backend-api) is completely unusable when accessed from mainland China through any proxy node. Cloudflare returns 403 with cf-mitigated: challenge header, blocking all requests before they reach ChatGPT backend.

Root Cause

Cloudflare detects Node.js native fetch TLS fingerprint (JA3/JA4) as non-browser traffic and triggers a JS Challenge. This happens regardless of:

  • Proxy node location (tested Japan 94.177.131.104, Canada 23.132.28.49, multiple IPs)
  • Request headers (even with full browser User-Agent and Sec-CH-UA headers)
  • OAuth token validity (token valid with 200+ hours remaining)
  • Transport mode (both HTTP/SSE and WebSocket get 403)

Key evidence: Using Python cloudscraper library (which mimics Chrome TLS fingerprint) with the same proxy and same OAuth token returns 200 OK successfully. This confirms the issue is purely TLS fingerprint-based detection.

Error Messages Observed

  1. LLM request failed: DNS lookup for the provider endpoint failed. — misleading; actually Cloudflare 403 HTML response
  2. Authentication failed with an HTML 403 response from the provider. — Cloudflare challenge, not auth failure
  3. LLM request failed: network connection error. — timeout variant
  4. blocked URL fetch (url-fetch) target=https://chatgpt.com/backend-api/responses reason=Blocked hostname or private/internal/special-use IP address — SSRF guard blocks VPN fake-DNS IPs (e.g. 198.x.x.x from Loon/QX)

Additional Context

  • Related issue: #64092 (error classification for 403)
  • Related PR: #67642 (classify Cloudflare HTML error pages)
  • The SSRF guard blocking fake-DNS IPs (issue #4 above) is a separate but compounding problem. Setting request.allowPrivateNetwork: true resolves that layer, but the Cloudflare TLS fingerprint block remains.

Workaround

Deploy a local Python reverse proxy using cloudscraper to bypass Cloudflare TLS fingerprint detection:

  1. Install cloudscraper:

    python3 -m venv /tmp/cftest && /tmp/cftest/bin/pip install cloudscraper
  2. Run a local HTTP reverse proxy (port 18792) that forwards requests to chatgpt.com/backend-api via cloudscraper with Chrome TLS fingerprint emulation.

  3. Configure OpenClaw to route openai-codex through the local proxy:

    {
      "models": {
        "providers": {
          "openai-codex": {
            "baseUrl": "http://127.0.0.1:18792",
            "models": [],
            "request": {
              "allowPrivateNetwork": true
            }
          }
        }
      }
    }
  4. Restart gateway: openclaw gateway restart

Suggested Fix

Consider one of:

  1. Add browser-like TLS fingerprint emulation to the openai-codex provider HTTP client (e.g. via undici with custom TLS settings or a bundled tls-client)
  2. Provide a built-in request.tlsProfile option (e.g. "chrome") for providers that need Cloudflare bypass
  3. Document this limitation and the workaround for users behind proxies/VPNs in regions where Cloudflare challenges are triggered

extent analysis

TL;DR

Deploy a local Python reverse proxy using cloudscraper to bypass Cloudflare TLS fingerprint detection for the openai-codex provider.

Guidance

  • To verify the issue, check the request headers and TLS fingerprint sent by the Node.js native fetch, and compare it with the fingerprint sent by the cloudscraper library.
  • Consider implementing browser-like TLS fingerprint emulation in the openai-codex provider HTTP client to bypass Cloudflare detection.
  • Configure OpenClaw to route openai-codex requests through the local proxy by setting the baseUrl to http://127.0.0.1:18792 and enabling allowPrivateNetwork.
  • Restart the gateway after configuring the proxy to ensure the changes take effect.

Example

{
  "models": {
    "providers": {
      "openai-codex": {
        "baseUrl": "http://127.0.0.1:18792",
        "models": [],
        "request": {
          "allowPrivateNetwork": true
        }
      }
    }
  }
}

Notes

The provided workaround using cloudscraper is a temporary solution and may not be suitable for production environments. A more permanent fix would involve modifying the openai-codex provider to emulate a browser-like TLS fingerprint.

Recommendation

Apply the workaround by deploying a local Python reverse proxy using cloudscraper to bypass Cloudflare TLS fingerprint detection, as it provides a functional solution to the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug] openai-codex provider blocked by Cloudflare JS Challenge when accessed via proxy in mainland China [1 pull requests, 1 participants]