Pin fetch to bundled undici for undici higher versions compatibility#4238
Conversation
wenshao
left a comment
There was a problem hiding this comment.
[Critical] DashScopeOpenAICompatibleProvider.buildClient() (dashscope.ts:98) overrides the parent method but is missing the fetch: undiciFetch pin added in this PR.
DashScope is the only subclass that overrides buildClient(). Users with proxy configured + DashScope will still encounter the invalid onError method error this PR aims to fix.
Fix: add fetch: undiciFetch as unknown as typeof fetch to the new OpenAI({...}) call in dashscope.ts:113 (matching the fix in default.ts:70).
[Critical] apiPreconnect.test.ts:57 uses vi.stubGlobal('fetch', mockFetch) which will not intercept the import { fetch } from 'undici' added by this PR.
17+ test cases will pass vacuously — mockFetch will never be called.
Fix: replace vi.stubGlobal('fetch', mockFetch) with vi.mock('undici', ...) to stub the module's fetch export.
— DeepSeek/deepseek-v4-pro via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
[Critical] 15/26 apiPreconnect tests are broken. The source now calls undiciFetch (imported from the undici module), but the test file still mocks globalThis.fetch via vi.stubGlobal('fetch', mockFetch). The mock never intercepts the actual call, so all assertions on mockFetch fail. Fix: mock the undici module's fetch export instead of the global.
[Critical] Fix is incomplete — dashscope.ts and anthropicContentGenerator.ts lack fetch pinning. dashscope.ts overrides buildClient() and constructs new OpenAI({…}) without fetch: undiciFetch, so proxy DashScope users still hit the invalid onError method crash. anthropicContentGenerator.ts passes a bundled-undici ProxyAgent dispatcher to the Anthropic SDK but doesn't pin fetch, creating the same mismatch. Add fetch: undiciFetch to both constructors, or centralize the pinning into buildRuntimeFetchOptions so all call sites are covered.
— glm-5.1 via Qwen Code /review
…rsion mismatch for review of QwenLM#4238 When a custom dispatcher (ProxyAgent) is passed, pin fetch to the bundled undici's implementation so both share the same undici version. Without this, Node's built-in fetch (e.g. undici v8) rejects a ProxyAgent from the bundled undici (e.g. v6) with "invalid onError method".
|
@wenshao updated according to review comments. |
wenshao
left a comment
There was a problem hiding this comment.
[Critical] Anthropic proxy bypass — anthropicContentGenerator.ts:213 spreads ...runtimeOptions (containing { fetchOptions: { dispatcher }, fetch: undiciFetch }) into new Anthropic(). The Anthropic SDK only stores fetch — fetchOptions is silently discarded. When the SDK calls fetch(url, req), req has no dispatcher, so undiciFetch runs without a proxy dispatcher. This is a regression for Anthropic proxy users. Fix: bind the dispatcher to the fetch function before passing it to the SDK.
[Critical] No test for fetch: undiciFetch pin in default.ts — The primary behavioral change (unconditional fetch pinning) has zero test coverage in default.test.ts. If the pin is accidentally removed, all tests still pass.
— mimo-v2.5-pro via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
[Suggestion] packages/core/src/core/openaiContentGenerator/provider/default.test.ts — missing test assertion for fetch in buildClient()
buildClient() now passes fetch: undiciFetch to the OpenAI constructor, but the existing test at ~line 127 uses expect.objectContaining({ apiKey, baseURL, timeout, maxRetries, defaultHeaders }) which passes even if fetch is absent. If someone accidentally removes the fetch: undiciFetch line, no regression test would catch it.
Suggested fix: add an explicit assertion:
expect(OpenAI).toHaveBeenCalledWith(
expect.objectContaining({ fetch: expect.any(Function) }),
);— DeepSeek/deepseek-v4-pro via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅ — mimo-v2.5-pro via Qwen Code /review
But if no proxy is set here, there is no fetch or dispatcher paased. |
wenshao
left a comment
There was a problem hiding this comment.
Suggestion (out of scope): gitUtils.ts 和 setupGithubCommand.ts 存在相同的 undici 版本不匹配模式(将捆绑 undici v6 的 ProxyAgent/dispatcher 传递给 Node 内置的 fetch),但此 PR 未处理。可在后续 PR 中修复。
— DeepSeek/deepseek-v4-pro via Qwen Code /review
| @@ -596,7 +603,13 @@ function buildFetchOptionsWithDispatcher( | |||
| // 300s bodyTimeout. This is sufficient for all current model streaming responses. | |||
There was a problem hiding this comment.
[Suggestion] Anthropic SDK 静默丢弃 fetchOptions.dispatcher。
buildFetchOptionsWithDispatcher 为两个 SDK 都返回 { fetchOptions: { dispatcher }, fetch: undiciFetch },但 Anthropic SDK(@anthropic-ai/sdk)构造函数不支持 fetchOptions——它只存储 fetch。dispatcher 被 Anthropic 路径静默丢弃。
这不是此 PR 引入的问题(PR 之前 dispatcher 就已经被返回了),但 fetch: undiciFetch 的显式添加使这种不对称性更加明显——未来维护者可能会看到这段代码,并认为 Anthropic 的代理路径是正常的,而实际上并非如此。
| // 300s bodyTimeout. This is sufficient for all current model streaming responses. | |
| // Note: Anthropic SDK does not support constructor-level fetchOptions, | |
| // so the dispatcher is only effective for OpenAI SDK calls. | |
| return { fetchOptions: { dispatcher }, fetch: undiciFetch }; |
— DeepSeek/deepseek-v4-pro via Qwen Code /review
|
@wenshao Do i need to modify |
wenshao
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: CI failing: Test (windows-latest, Node 22.x). — gpt-5.5 via Qwen Code /review
|
The Windows CI Test is experiencing process spawn timeouts, which is not caused by this PR. |
wenshao
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: CI failing: Test (windows-latest, Node 22.x). — qwen-latest-series-invite-beta-v28 via Qwen Code /review
Windows CI 失败分析失败的测试: 根因: Windows 路径分隔符问题。 return path.sep === '/' ? rel : rel.split(path.sep).join('/');但在 Windows 上 这个失败跟本 PR 的改动无关 — PR 修改的是 undici/fetch 兼容性,而失败的测试是一个完全不相关的 workspace glob 路由。这是 main 分支上已有的 Windows 兼容性问题。 建议修复: 在 // 改前
return path.sep === '/' ? rel : rel.split(path.sep).join('/');
// 改后
return rel.split(/[\\/]/).join('/');这样无论 |
|
@wenshao it is better to fix Windows CI problem of |
|
I took another pass on the latest diff. The current approach looks like the right direction to me: keeping A few small follow-ups before merge:
Assuming the Windows CI rerun passes or is confirmed unrelated, I don't see any remaining code-level blocker in this PR. |
|
@yiliang114 1. updated description to metion about #4035 #3914 #4274 |
| // breaks dispatcher handler-interface checks (`invalid onError method`). | ||
| // The no-proxy branch above intentionally skips this so the runtime's | ||
| // built-in fetch continues to be used when no dispatcher is involved. | ||
| return { fetchOptions: { dispatcher }, fetch: undiciFetch }; |
There was a problem hiding this comment.
[Suggestion] Missing success-path debug log. buildFetchOptionsWithDispatcher logs failures extensively (debugLogger.warn + console.error), but when dispatcher creation succeeds and fetch is pinned to undiciFetch, no log is emitted. When debugging proxy issues in production, there's no way to confirm the fetch override was injected without reading source code.
Consider adding a debug log before the return:
| return { fetchOptions: { dispatcher }, fetch: undiciFetch }; | |
| debugLogger.debug( | |
| `Proxy dispatcher created for ${extractHostnameFromProxyUrl(proxyUrl)}, fetch pinned to bundled undici`, | |
| ); | |
| return { fetchOptions: { dispatcher }, fetch: undiciFetch }; |
— qwen-latest-series-invite-beta-v34 via Qwen Code /review
|
看了一下 Windows CI(attempt #5)的两个失败,都跟本 PR(pin undici fetch)无关,是 Windows 平台 pre-existing 的问题:
建议先把最新的 |
Node.js 26 bundles undici 8.x, which differs from the project's undici 6.x. Using Node's built-in fetch mixed with ProxyAgent/Client from the bundled undici causes handler-interface mismatches (e.g. 'invalid onError method').
…rsion mismatch for review of QwenLM#4238 When a custom dispatcher (ProxyAgent) is passed, pin fetch to the bundled undici's implementation so both share the same undici version. Without this, Node's built-in fetch (e.g. undici v8) rejects a ProxyAgent from the bundled undici (e.g. v6) with "invalid onError method".
…change back default.ts
e7acb11 to
fa5f896
Compare
|
rebased on |
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅ — qwen-latest-series-invite-beta-v34 via Qwen Code /review
Node.js 26 (on ArchLinux installed by
sudo pacman -S nodejs) bundles undici 8.x, which differs from the project's undici 6.x. Using Node's built-in fetch mixed with ProxyAgent/Client from the bundled undici causes handler-interface mismatches (e.g. 'InvalidArgumentError: invalid onError method').This PR should fix #4035 #3914 #4274
Summary
Validation
npm install npm run build sudo npm link OPENAI_BASE_URL=https://api.deepseek.com OPENAI_MODEL=deepseek-v4-flash OPENAI_API_KEY=sk-xxxx qwen # Ask questions in qwen[API Error: Connection error. (cause: fetch failed)]Before:
Scope / Risk
Testing Matrix
Testing matrix notes:
Linked Issues / Bugs
qwen-code 0.15.11fails withConnection error. (cause: fetch failed)on Node.js 26 unlessfetchOptions.dispatcheris removed #4274