From 85aa8b0985c8f3cb8801efa5141114a0ab0f6a83 Mon Sep 17 00:00:00 2001 From: changjiaoxigua <51255010+changjiaoxigua@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:02:05 +0800 Subject: [PATCH] fix: add File polyfill for Node < 20 to prevent startup deadlock with proxy (#442) When a proxy is configured, configureGlobalAgents() loads undici to set a global dispatcher. However, undici v7.24.6 requires Node.js >= 20.18.1 and references globalThis.File at module evaluation time for webidl type assertions. Node 18 lacks the File global, causing ReferenceError inside the bundled __commonJS require chain, which deadlocks due to unresolved circular dependencies in the module initialization. Fix by polyfilling globalThis.File early in cli.tsx entrypoint, before any undici code loads. Try node:buffer.File (available in Node 18.13+), fallback to minimal Blob-based stub. Fixes: bun run start hangs indefinitely when HTTP_PROXY/HTTPS_PROXY is set Co-authored-by: Claude Opus 4.6 --- src/entrypoints/cli.tsx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/entrypoints/cli.tsx b/src/entrypoints/cli.tsx index 92efc64e..3a00abbe 100644 --- a/src/entrypoints/cli.tsx +++ b/src/entrypoints/cli.tsx @@ -8,6 +8,34 @@ import { validateProviderEnvOrExit, } from '../utils/providerValidation.js' +// OpenClaude: polyfill globalThis.File for Node < 20. +// undici v7 references `File` at module evaluation time (webidl type +// assertions). Node 18 lacks the global, causing a ReferenceError inside +// the bundled __commonJS require chain which deadlocks the process when a +// proxy is configured (configureGlobalAgents → require_undici). +// eslint-disable-next-line custom-rules/no-top-level-side-effects +if (typeof globalThis.File === 'undefined') { + try { + // Node 18.13+ exposes File in node:buffer but not as a global. + // eslint-disable-next-line @typescript-eslint/no-require-imports + const { File: NodeFile } = require('node:buffer') + // @ts-expect-error -- polyfilling missing global + globalThis.File = NodeFile + } catch { + // Absolute fallback: stub so `MakeTypeAssertion(File)` doesn't throw. + // @ts-expect-error -- minimal polyfill + globalThis.File = class File extends Blob { + name: string + lastModified: number + constructor(parts: BlobPart[], name: string, opts?: FilePropertyBag) { + super(parts, opts) + this.name = name + this.lastModified = opts?.lastModified ?? Date.now() + } + } + } +} + // OpenClaude: disable experimental API betas by default. // Tool search (defer_loading), global cache scope, and context management // require internal API support not available to external accounts → 500.