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.
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
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
This one is unusually well-localised by the probe: the WebSocket handshake is byte-exact, with a correct RFC 6455
Sec-WebSocket-Acceptvalue 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 isundefinedat the point.destroy()is called on it.Why these are worth taking together
Both are
Cannot read properties of undefinedon an object that the package expects the runtime to have provided — a socket inws's case, something in the request path forundici. Both packages sit directly onnode:net/node:httpinternals 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.compilePackagesentry, and exercise the primary documented use —request()against a local server for undici; aWebSocketServerplus a client connection for ws. Compare againstnode --experimental-strip-typeson Node 26.5.1.Note on how these were found
Both packages passed a static pre-screen for every known blocker shape (
export =+ namespace, conditionalrequire,new Functionwith 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.