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
{{ message }}
Repository navigation
undici request() and ws server both throw 'Cannot read properties of undefined' at the transport layer despite compiling cleanly #10797
Two HTTP/WebSocket packages compile from real source but fail at runtime, both before any network I/O completes. Filing together because both are transport-layer failures found in the same probe and may share a cause; split if they diverge.
Found on origin/main (v0.5.1617), --profile perry-dev, Linux x64, versions pinned to each binding's [bindings.<pkg>.upstream] block.
undici 8.9.0 — 112 modules, compiles, then throws before connecting
TypeError: Cannot read properties of undefined (reading 'length')
Thrown from request()before any TCP connect is attempted, so it is not a socket or protocol problem — something is undefined in the request-construction path.
ws 8.21.1 — handshake provably correct, crash immediately after
TypeError: Cannot read properties of undefined (reading 'destroy')
This one is unusually well-localised by the probe: the WebSocket handshake is byte-exact, with a correct RFC 6455 Sec-WebSocket-Accept value computed and sent. The server then crashes before the 'connection' event fires. So the protocol logic works and the failure is in what happens to the socket object immediately afterwards — something that should be a socket is undefined at the point .destroy() is called on it.
Why these are worth taking together
Both are Cannot read properties of undefined on an object that the package expects the runtime to have provided — a socket in ws's case, something in the request path for undici. Both packages sit directly on node:net/node:http internals rather than on the public surface. If Perry is under-populating an object that Node provides, one fix may resolve both. If they turn out to be unrelated, this should be split into two issues.
Reproduction
Both: install the pinned version, add a perry.compilePackages entry, and exercise the primary documented use — request() against a local server for undici; a WebSocketServer plus a client connection for ws. Compare against node --experimental-strip-types on Node 26.5.1.
Note on how these were found
Both packages passed a static pre-screen for every known blocker shape (export = + namespace, conditional require, new Function with class expressions, bound-export native heritage, require.main). They failed anyway. That is the screen working as designed — it narrows where to look and predicts nothing about what it has not seen before — and it is why compiling is not optional before declaring a binding removable.
Re-examine these two before investing in them — two of the four blockers from the same probe wave have now turned out not to be what they were reported as.
Both false readings share a cause: the probe exercised these packages in ways that differ from how a program actually consumes them — routing around a hard failure with a partial-codegen flag in one case, reaching into package internals in the other. Neither symptom was a lie; both were real observations of something other than the stated defect.
So before anyone spends a day on undici or ws:
Reproduce each from a plain, ordinary consumer — a normal import/require of the package's public entry point, no partial-codegen flags, no reaching into internals, pinned to the version in [bindings.<pkg>.upstream].
The underlying defects may well be real — undici's failure before any TCP connect, and ws's correct handshake followed by an undefined socket, are both specific and plausible. This is not a claim that they are artifacts. It is that two of four from this batch were, so the reproduction should be re-established from a clean consumer before the investigation is scoped.
Method note worth keeping
A probe that reports "package X fails with error E" is reporting what it observed, not necessarily what a user would hit. The observation is still valuable — it is what surfaced #10806 and #10417's reach — but the framing "this package is blocked by E" carries an inference the probe did not test. Recording the fixture alongside the error, not just the error, would have made both of these visible immediately.
Re-established both probes from clean public-entry consumers on current main (47ade47327), with the package versions from this issue and no partial-codegen flags or internal-file imports. Neither reported exception reproduces.
ws@8.21.1
Fixture imports { WebSocketServer, WebSocket } from ws, starts a local server, and connects a public client. It compiles and runs, but now stops at:
Error: Unexpected server response: 426
The original post-handshake undefined.destroy crash does not occur.
undici@8.9.0
Fixture imports { request } from undici and requests a local node:http server. The clean 111-module source graph reaches link, then is blocked by the llhttp WebAssembly dependency:
undefined reference to js_webassembly_module_new
The original pre-connect undefined.length runtime failure therefore is not currently reachable from the supported public consumer path.
These are distinct current blockers, not evidence for the transport-object hypothesis in the issue, and they should be split/reframed before code changes are proposed. I am not opening a speculative/no-op PR for #10797.
Closing this combined report because neither stated undefined failure reproduces from clean public-entry consumers on current main. The current, distinct blockers are now tracked separately as #10888 (ws, HTTP 426) and #10889 (undici, missing WebAssembly runtime symbol).
Activity
proggeramlug commentedon Sep 20, 2026
Re-examine these two before investing in them — two of the four blockers from the same probe wave have now turned out not to be what they were reported as.
dist/esmworks byte-identically to Node; the reported symptom came from routing around Cross-module inlining copiesEnum.Memberreferences into the importer: "enum member X.Y not found in enums table" #10417 (cross-module enum inlining) withPERRY_ALLOW_PARTIAL_CODEGEN=1, which produces a different failure. Cheerio's real blocker is Cross-module inlining copiesEnum.Memberreferences into the importer: "enum member X.Y not found in enums table" #10417.Stream) — theextends Streamgap was real and is fixed (PR fix(runtime): dispatch node:stream super() through the legacy Stream base #10805), but theTypeError: is not a constructorreported in the issue did not reproduce for any genuineextends Streamshape, including nodemailer's ownXOAuth2path, which already worked on pristinemain. That exact error came from something else entirely: requiring a compiled package's internal file from outside the package yields a class export withtypeof "object", which reproduces for a plain class with no heritage at all. Filed as perry.compilePackages: a compiled package's internal file, required from OUTSIDE the package, exports a class as typeof object (not function) — new X() throws 'is not a constructor' #10806.What that implies for this issue
Both false readings share a cause: the probe exercised these packages in ways that differ from how a program actually consumes them — routing around a hard failure with a partial-codegen flag in one case, reaching into package internals in the other. Neither symptom was a lie; both were real observations of something other than the stated defect.
So before anyone spends a day on undici or ws:
import/requireof the package's public entry point, no partial-codegen flags, no reaching into internals, pinned to the version in[bindings.<pkg>.upstream].ws. Its reported failure isCannot read properties of undefined (reading 'destroy')immediately after a byte-correct handshake — andwsis exactly the kind of package where a server implementation reaches across internal module boundaries. If the probe's fixture touched internals, perry.compilePackages: a compiled package's internal file, required from OUTSIDE the package, exports a class as typeof object (not function) — new X() throws 'is not a constructor' #10806 could produce a symptom like this.Enum.Memberreferences into the importer: "enum member X.Y not found in enums table" #10417 for both, as cheerio's case showed a hard compile failure can be routed around into a misleading runtime error.The underlying defects may well be real —
undici's failure before any TCP connect, andws's correct handshake followed by an undefined socket, are both specific and plausible. This is not a claim that they are artifacts. It is that two of four from this batch were, so the reproduction should be re-established from a clean consumer before the investigation is scoped.Method note worth keeping
A probe that reports "package X fails with error E" is reporting what it observed, not necessarily what a user would hit. The observation is still valuable — it is what surfaced #10806 and #10417's reach — but the framing "this package is blocked by E" carries an inference the probe did not test. Recording the fixture alongside the error, not just the error, would have made both of these visible immediately.
proggeramlug commentedon Sep 21, 2026
Re-established both probes from clean public-entry consumers on current
main(47ade47327), with the package versions from this issue and no partial-codegen flags or internal-file imports. Neither reported exception reproduces.ws@8.21.1Fixture imports
{ WebSocketServer, WebSocket }fromws, starts a local server, and connects a public client. It compiles and runs, but now stops at:The original post-handshake
undefined.destroycrash does not occur.undici@8.9.0Fixture imports
{ request }fromundiciand requests a localnode:httpserver. The clean 111-module source graph reaches link, then is blocked by the llhttp WebAssembly dependency:The original pre-connect
undefined.lengthruntime failure therefore is not currently reachable from the supported public consumer path.These are distinct current blockers, not evidence for the transport-object hypothesis in the issue, and they should be split/reframed before code changes are proposed. I am not opening a speculative/no-op PR for #10797.
proggeramlug commentedon Sep 21, 2026
Closing this combined report because neither stated
undefinedfailure reproduces from clean public-entry consumers on currentmain. The current, distinct blockers are now tracked separately as #10888 (ws, HTTP 426) and #10889 (undici, missing WebAssembly runtime symbol).