Skip to content

Bug Report: Codex Desktop (Windows) Browser Use — Persistent Statsig Initialization Failure Within a Session #45366

Open

Description

@leonwang97-rgb

What version of the Codex App are you using (From “About Codex” dialog)?

ChatGPT Powered by Codex & OWL 版本 26.908.40834

What subscription do you have?

ChatGPT Business PRO

What platform is your computer?

win11

What issue are you seeing?

Bug Report: Codex Desktop (Windows) Browser Use — Persistent Statsig Initialization Failure Within a Session

Summary

Browser Use / Computer Use intermittently and persistently fails within a single Codex Desktop session with:

Unable to load browser request-header policy. Retry the browser command.

and occasionally:

Error: nodeRepl.fetch request failed

Once this failure occurs in a given session/thread, it fails on every subsequent retry within that same session, even after "Retry the browser command," even after the underlying network connectivity is fully healthy and verified. Starting a brand-new Codex session/thread immediately resolves the issue — the very next browser action succeeds in ~2 seconds, with no configuration changes.

This strongly suggests the root cause is a module-level singleton in the Statsig client initialization inside the bundled browser-service.mjs that gets stuck in a failed/incomplete state after a single unlucky initialization attempt (e.g., during a brief network hiccup), and is never retried or reset for the lifetime of that session/thread.

Environment

  • OS: Windows 11 Pro x64
  • ChatGPT/Codex Desktop version: 26.908.4834.0 (also reproduced on 26.903.9818.0 before an app auto-update)
  • Browser plugin build: 26.908.40834 (also reproduced on 26.903.71938)
  • Computer Use / Unified Computer Use plugin: same build as above
  • Node runtime bundled with Codex: v24.20.0 (@oai/sky, @oai/cua-repl)
  • [windows].sandbox: reproduced under both elevated and unelevated
  • Local network setup: sing-box (v2rayN) SOCKS/HTTP proxy on 127.0.0.1:10808, later upgraded to full TUN-mode (system-level virtual network adapter, transparent proxying — this fix was not what resolved the issue, see below)
  • Network context: issue reproduced on both a home Wi-Fi network and a corporate (DomainAuthenticated) Wi-Fi network

Exact reproduction steps

  1. Open Codex Desktop, start any task/thread.
  2. Ask it to use Browser Use / Computer Use to navigate to any URL (e.g. https://example.com) and read the title.
  3. If the first attempt in that thread happens to hit a transient network condition (see "Suspected trigger" below), the browser tool call fails with:
    {"apps":[],"browsers":[],"errors":["Browsers: Error: Unable to load browser request-header policy. Retry the browser command."]}
    with a full stack trace ending in:
    Error: Unable to load browser request-header policy. Retry the browser command.
        at ...\kernel.js:1595:16
        at Object.settle (...\worker-runtime.js:396:5)
        at ...\kernel.js:1871:11
        at handleInputFrame (...\worker-runtime.js:418:7)
        at Socket.<anonymous> (...\worker-runtime.js:434:9)
    
  4. Every subsequent browser action in the same thread fails identically, even minutes/hours later, even after:
    • Retrying multiple times ("Retry the browser command" as instructed by the error itself)
    • Fully restarting the Codex Desktop app
    • Verifying and fixing all local network/proxy/DNS/firewall/sandbox configuration
    • Waiting well past any plausible timeout window
  5. Opening a brand-new Codex thread and repeating the exact same browser action succeeds immediately (observed: 2.28 seconds wall time for a full createBrowserTab + getAXState), with zero configuration changes between the failing and succeeding attempt (only ~20 seconds elapsed).

Evidence collected

1. Timeline proving session-scoped persistence, not environment

  • Failing attempt (existing thread, created several days earlier): local time 13:35:38–13:37:07, ~20s timeout per call, error as above.
  • New thread created at local time 13:37:27 (20 seconds after the previous failure) — identical browser action, identical machine, identical network state — succeeded in 2.28s and correctly returned:
    {"title":"Example Domain","url":"https://example.com/"}

This same pattern (fail-forever-in-old-thread / succeed-immediately-in-new-thread) was observed on multiple separate occasions over several days, including across app restarts, network changes (home Wi-Fi → corporate Wi-Fi), and proxy reconfiguration — none of which affected the outcome. Only creating a new thread reliably resolved it.

2. Root cause traced to a module-level singleton

Static analysis of the bundled runtime script plugins/cache/openai-bundled/browser/<version>/scripts/browser-service.mjs shows:

var Gp=1e4, Vr; // Vr is a module-level singleton, never reset
function rw(e){
  if (Vr != null) return Vr; // <-- returns the SAME instance forever, even if it failed to initialize
  ...
  return Vr = {
    client: new StatsigClient(sdkKey, user, {
      networkConfig: { api, sdkExceptionUrl, networkOverrideFunc: (i,s) => e.fetch(i,s) },
      ...
    }),
    initialized: false,
    ...
  };
}

async function initStatsig(e) {
  let t = rw(e);
  try {
    if (t.initialization == null) {
      t.initialization = t.client.initializeAsync({ timeoutMs: Gp }); // 10s timeout
      t.ready = t.initialization;
    }
    await t.initialization;
    await waitReady(t); // another 10s timeout
    t.initialized = true;
  } catch (r) {
    console.warn(r); // swallowed — no retry, no reset of Vr
  }
}

async function requireGate(e, gateName) {
  let t = rw(e); // ALWAYS returns the same (possibly broken) singleton
  if (t?.initialization == null)
    throw new Error("Browser request-header policy requires Statsig initialization.");
  await t.initialization;
  if (!(await waitReady(t))?.success || t.client.loadingStatus !== "Ready")
    throw new Error("Unable to load browser request-header policy. Retry the browser command.");
  ...
}

Key observations:

  • Vr (the Statsig client + init-state wrapper) is created once per process and cached in a module-level variable. It is never invalidated, retried, or torn down after a failed initialization.
  • If t.client.initializeAsync() does not reach loadingStatus === "Ready" within the module's own internal window (two chained 10-second timeouts = ~20s, matching the exact wall-time observed in every failing call), requireGate() throws the "Unable to load browser request-header policy" error — and every future call reuses the same broken Vr, so it throws the identical error every time, forever, for the lifetime of the hosting process.
  • The only way to obtain a fresh Vr (and thus a fresh Statsig init attempt) is to get a brand-new JS execution context — which happens automatically when Codex starts a new thread (it spins up a new node_repl.exe → trusted-worker.js process pair with a fresh temp directory, e.g. %TEMP%\.tmpXXXXXX\), but never happens within an existing thread, no matter how many times the user retries.

3. Ruled-out causes (extensive verification performed)

Over several hours of investigation across multiple failure occurrences, the following were verified as NOT the cause, despite initially appearing plausible:

  • DNS poisoning / hijacking on the local network — confirmed present in one instance (plaintext DNS to ab.chatgpt.com returned incorrect IPs even against 8.8.8.8; DoH resolution returned correct Cloudflare IPs). This was a real, separate issue and was fixed, but did not fully resolve the recurring failure.
  • Local HTTP/SOCKS proxy not being honored by node_repl's env/env_vars config in config.toml — confirmed that Codex regenerates and overwrites the entire [mcp_servers.node_repl.env] block on every app start, silently discarding any manually added HTTPS_PROXY/NODE_USE_ENV_PROXY entries. This is real and reproducible, but not the root cause of this specific bug (see below).
  • Stale OS-level environment variable propagation (explorer.exe caching an old environment snapshot after setx-style updates) — real Windows behavior, fixed by restarting explorer.exe, but not the root cause here.
  • Windows-native sandbox (elevated mode) network isolation — investigated codex_sandbox_offline_block_outbound / ..._loopback_tcp / ..._loopback_udp firewall rules scoped to the CodexSandboxOffline local user (SID ending -1003). Confirmed via Get-CimInstance Win32_Process | Invoke-CimMethod GetOwner that none of the actual browser-related processes (node_repl.exe, its child trusted-worker.js node process) run under this restricted identity — they run under the normal user account. Switching [windows].sandbox from elevated to unelevated did not change the failure pattern.
  • TUIC/QUIC proxy protocol instability on a corporate network — stress-tested with 10 concurrent fetch() calls to ab.chatgpt.com through the exact same Node runtime bundled with Codex; all 10 succeeded in under 1 second each, with zero failures, both on a fresh proxy client and hours after prior activity.
  • General network flakiness — ruled out by the fact that a brand-new thread succeeds in ~2 seconds immediately after an old thread fails, on identical network conditions.

4. Process architecture notes (for engineering triage)

  • node_repl.exe (parent) spawns a child node.exe --eval <realpath-shim> <tempdir>\trusted-worker.js <workdir> process per thread/session.
  • trusted-worker.js dynamically import()s the trusted browser-service.mjs module referenced by NODE_REPL_TRUSTED_SERVICES in the environment.
  • The Statsig client inside browser-service.mjs receives its fetch implementation via a constructor-injected e.fetch, which in the observed build resolves to the global Node.js fetch (confirmed via kernel.js: if (typeof fetch !== "undefined") runtimeContext.fetch = fetch;).
  • This global fetch call is a different code path from the higher-level nodeRepl.fetch(...) API (which instead serializes the request and forwards it via stdout IPC to the parent process using an authenticated_fetch message type, for cookie/auth-aware fetches). The Statsig initialization request does not go through this IPC path — it calls the global fetch directly inside the trusted-worker.js process.

Suspected trigger for the first failure in a thread

We could not conclusively pin down what causes the first Statsig initializeAsync() call to fail (all manual reproduction attempts with a fresh process succeeded), but candidates include:

  • A transient network blip during the ~20s initialization window (e.g., proxy client restart, brief DNS resolution failure, Wi-Fi roaming) that happens to coincide with the very first browser action taken in a new thread.
  • Corporate network deep packet inspection / stateful firewall intermittently interfering with the specific TLS/HTTP client fingerprint used by this request, distinct from ordinary fetch() traffic (unconfirmed, speculative).

Regardless of the trigger, the actual bug is that a single failed Statsig initialization permanently and silently poisons the module-level singleton for the rest of the thread's lifetime, with no retry, backoff, or self-healing logic, and the user-facing error message ("Retry the browser command") is actively misleading because retrying within the same thread can never succeed.

Suggested fix

In browser-service.mjs's Statsig init wrapper:

  1. On initializeAsync() failure or loadingStatus !== "Ready" after the internal timeout, invalidate Vr (set it back to null, or store an explicit initFailedAt timestamp) so the next call to rw(e) / requireGate(e, ...) creates a fresh StatsigClient and retries initialization, instead of permanently returning the broken cached instance.
  2. Consider adding exponential backoff or at least a short cooldown (e.g. re-attempt after N seconds) rather than an unconditional retry-on-every-call, to avoid hammering the endpoint if it's genuinely down.
  3. Alternatively/additionally, surface a more actionable error message distinguishing "first-time init failed, will retry automatically" from the current text, which implies a user retry will help when it currently cannot.

Workaround (for other users hitting this)

If Browser Use / Computer Use fails with "Unable to load browser request-header policy" and retrying within the same conversation does not help: start a new Codex thread/conversation and repeat the same browser action there. This has been 100% reliable in our testing (multiple occurrences, always resolved by a new thread, never by retrying in the same thread).

Attachments available on request

  • Full firewall connection logs (pfirewall.log) for the exact failure window, showing healthy proxy traffic (ALLOW TCP 127.0.0.1→127.0.0.1:10808) throughout, with no relevant DROP entries correlating to the failure.
  • Session JSONL transcripts showing the exact failing tool call, error stack trace, and the immediately-following successful call in a new thread, with millisecond-precision timestamps.
  • Extracted source excerpts from kernel.js, worker-runtime.js, trusted-worker.js, privileged-node-repl.js, and browser-service.mjs from the temp runtime directory, showing the singleton pattern and IPC architecture described above.

What steps can reproduce the bug?

  1. Open Codex Desktop, start any task/thread.
  2. Ask it to use Browser Use / Computer Use to navigate to any URL (e.g. https://example.com) and read the title.
  3. If the first attempt in that thread happens to hit a transient network condition (see "Suspected trigger" below), the browser tool call fails with:
    {"apps":[],"browsers":[],"errors":["Browsers: Error: Unable to load browser request-header policy. Retry the browser command."]}
    with a full stack trace ending in:
    Error: Unable to load browser request-header policy. Retry the browser command.
        at ...\kernel.js:1595:16
        at Object.settle (...\worker-runtime.js:396:5)
        at ...\kernel.js:1871:11
        at handleInputFrame (...\worker-runtime.js:418:7)
        at Socket.<anonymous> (...\worker-runtime.js:434:9)
    
  4. Every subsequent browser action in the same thread fails identically, even minutes/hours later, even after:
    • Retrying multiple times ("Retry the browser command" as instructed by the error itself)
    • Fully restarting the Codex Desktop app
    • Verifying and fixing all local network/proxy/DNS/firewall/sandbox configuration
    • Waiting well past any plausible timeout window
  5. Opening a brand-new Codex thread and repeating the exact same browser action succeeds immediately (observed: 2.28 seconds wall time for a full createBrowserTab + getAXState), with zero configuration changes between the failing and succeeding attempt (only ~20 seconds elapsed).

What is the expected behavior?

No response

Additional information

Codex-BrowserUse-Bug-Report-2026-09-14.md

Activity

  1. added
    bugSomething isn't working
    windows-osIssues related to Codex on Windows systems
    connectivityIssues involving networking or endpoint connectivity problems (disconnections)
    on Sep 14, 2026
  2. lcawliet commented on Sep 17, 2026

    @lcawliet

    This analysis matches what we see, with one important difference that your Vr-singleton theory explains well.

    Observed on Windows (Codex Desktop 26.908.9136, browser plugin 26.908.70816, Edge extension 1.26.901.11451):

    • Here the failure is deterministic, not transient. A brand-new thread, an app restart, an extension reinstall, a plugin-cache re-materialization and a native-host re-registration all reproduce it — so the "open a new thread" workaround does not help in this case.
    • The reason looks like response size rather than a transient network blip: replaying the SDK's cold-start initialize request returns HTTP 200, 4,774,624 bytes (dynamic_configs 1128 entries ≈ 95%, layer_configs 162, feature_gates 398; is_delta:false), while small responses on the same privileged authenticated_fetch path succeed.
    • node_repl.exe carries the error string nodeRepl.fetch response is too large alongside nodeRepl.fetch request failed / nodeRepl.fetch response read failed, which suggests a response-size cap on that bridge.

    So the singleton poisoning you describe still applies — but because every cold start re-requests the same multi-megabyte payload, the first initialization never has a chance to succeed, and a fresh Vr (new thread) cannot recover it either. That also matches the ~20 s wall time in every failing call (two chained 10 s timeouts).

    Full measurement and environment: #46129

  3. knightli commented on Sep 21, 2026

    @knightli

    I reproduced a closely related Statsig initialization failure on the current Desktop/CUA builds. After fixing proxy inheritance in the local bridge, the browser service logged Timeout of 10000ms expired while initializing /v1/initialize; the response was about 4.9 MB and took roughly 7–15 seconds through the local proxy. Temporarily raising both the initialization and network 10-second deadlines to 30 seconds in the current trusted browser service made cua.getState(), createBrowserTab, and getAXState succeed after a restart.

    So the local “retry keeps failing” behavior is in the same problem family as the module-level singleton described here, and it is amplified by the large response/10-second deadline. Starting a new thread can temporarily work because it refreshes the execution context, but the stronger upstream fix is to invalidate a failed client and retry with backoff instead of reusing a poisoned singleton. The error should also distinguish timeout, oversized response, proxy failure, and cached initialization failure.

    The local workaround is diagnostic only: ensure the active CUA process inherits the proxy environment and apply a capability-checked, reversible timeout adjustment to the current trusted service. It does not bypass request-header policy; Statsig must still initialize and reach Ready. It should not be treated as an official fix or implemented with a hard-coded old version path.

    ref: #46129 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbrowserbugSomething isn't workingconnectivityIssues involving networking or endpoint connectivity problems (disconnections)windows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions