You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On Node 26.x (and presumably any future major that ships a stricter undici), the first request through @qdrant/js-client-rest fails with:
UND_ERR_INVALID_ARG: invalid onError method
The cause appears to be a version mismatch between two undicis in the same process:
@qdrant/js-client-rest pins undici: ^6.23.0 and constructs an undici.Agent (in packages/js-client-rest/src/dispatcher.ts).
It passes that Agent as the dispatcher option to Node's built-in fetch(), which on Node 26 uses a newer undici internally.
The two undicis disagree on the dispatcher hook contract — the newer one validates onError strictly and rejects the v6 Agent's shape on first dispatch.
The client itself has no onError in source (I grepped dist/); the error originates inside undici's internal validation pipeline when fetch tries to use the externally-constructed Agent.
Environment
@qdrant/js-client-rest
1.18.0 (latest as of filing); declared ^1.17.0 by downstream
Node (broken)
26.0.0
Node (works)
22.x
OS
macOS 14 (also reported on Linux x86_64 per downstream notes)
Qdrant server
v1.17.0
Reproduction (constructed; not directly executed)
I encountered this through socraticode — an MCP server that wraps the qdrant client — not by running the minimal script below. The script is reconstructed from inspecting dispatcher.ts and should exercise the same failing code path. If a maintainer can confirm or refute the minimal repro, that'll tell us whether the failure is purely in the dispatcher path or whether it also requires socraticode-specific usage.
When this Agent (from the npm-installed undici@^6) is passed to Node's built-in fetch(), fetch routes the dispatch through Node's bundled undici. The bundled undici has tightened its dispatcher-hook validation between Node 22's vendored version and Node 26's — it now requires the request handler's onError to be a function with a specific signature, and the v6 Agent doesn't satisfy it.
This is structurally a "two undicis in one process" problem. As long as the client constructs its dispatcher with one undici version and fetch handles it with another, contract drift between them will keep surfacing as cryptic errors.
Suggested fixes (already in flight)
Two existing open PRs in this repo would likely both fix this:
undici major upgrade #123 — undici major upgrade. If the client moves to undici 7 (or whatever Node 26 ships), both sides agree on the contract.
Inject fetch into REST transport #128 — Inject fetch into REST transport. If callers can supply their own fetch (e.g. from the same undici package the client uses), there's no two-undici problem at all.
I'd defer to maintainer judgment on which to prioritize, but #128 in particular sidesteps the entire class of bug — every future undici contract drift becomes a non-issue because the user controls both sides.
If neither lands soon, a minimal interim mitigation might be to import undici.fetch and undici.Agent from the same module (import { Agent, fetch } from 'undici') and use both, instead of mixing the npm Agent with Node's global fetch. That keeps the two sides on the same undici version regardless of the Node version.
Downstream impact
This bug affects any tool built on @qdrant/js-client-rest that runs under Node 26+. In our case it's socraticode (an MCP server that wraps a Qdrant-backed codebase index). I'm filing a separate PR there that refuses Node 26+ with a clear error message until this is resolved upstream — but it'd be much better to fix the root cause.
Happy to test a candidate fix on Node 26.x against a real workload.
Summary
On Node 26.x (and presumably any future major that ships a stricter undici), the first request through
@qdrant/js-client-restfails with:The cause appears to be a version mismatch between two undicis in the same process:
@qdrant/js-client-restpinsundici: ^6.23.0and constructs anundici.Agent(inpackages/js-client-rest/src/dispatcher.ts).dispatcheroption to Node's built-infetch(), which on Node 26 uses a newer undici internally.onErrorstrictly and rejects the v6 Agent's shape on first dispatch.The client itself has no
onErrorin source (I greppeddist/); the error originates inside undici's internal validation pipeline when fetch tries to use the externally-constructed Agent.Environment
@qdrant/js-client-rest^1.17.0by downstreamReproduction (constructed; not directly executed)
Expected (observed on Node 22.x)
Actual (Node 26.0.0)
Root cause (best guess)
packages/js-client-rest/src/dispatcher.tsdoes:When this Agent (from the npm-installed
undici@^6) is passed to Node's built-infetch(), fetch routes the dispatch through Node's bundled undici. The bundled undici has tightened its dispatcher-hook validation between Node 22's vendored version and Node 26's — it now requires the request handler'sonErrorto be a function with a specific signature, and the v6 Agent doesn't satisfy it.This is structurally a "two undicis in one process" problem. As long as the client constructs its dispatcher with one undici version and
fetchhandles it with another, contract drift between them will keep surfacing as cryptic errors.Suggested fixes (already in flight)
Two existing open PRs in this repo would likely both fix this:
fetch(e.g. from the sameundicipackage the client uses), there's no two-undici problem at all.I'd defer to maintainer judgment on which to prioritize, but #128 in particular sidesteps the entire class of bug — every future undici contract drift becomes a non-issue because the user controls both sides.
If neither lands soon, a minimal interim mitigation might be to import
undici.fetchandundici.Agentfrom the same module (import { Agent, fetch } from 'undici') and use both, instead of mixing the npmAgentwith Node's globalfetch. That keeps the two sides on the same undici version regardless of the Node version.Downstream impact
This bug affects any tool built on
@qdrant/js-client-restthat runs under Node 26+. In our case it's socraticode (an MCP server that wraps a Qdrant-backed codebase index). I'm filing a separate PR there that refuses Node 26+ with a clear error message until this is resolved upstream — but it'd be much better to fix the root cause.Happy to test a candidate fix on Node 26.x against a real workload.