Problem
withRemoteRefresh
of next-remote-refresh
sometimes throws an error:
error - Failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error
Error: Cannot destructure property 'port' of 'createServer(...).address(...)' as it is null.
at async Object.loadConfig [as default] (file:///home/projects/nextjs-y1ndyw/node_modules/next/dist/server/config.js:69:36)
at async NextServer.prepare (file:///home/projects/nextjs-y1ndyw/node_modules/next/dist/server/next.js:130:24)
at async eval (file:///home/projects/nextjs-y1ndyw/node_modules/next/dist/cli/next-dev.js:343:17)
TypeError: Cannot read properties of null (reading 'close')
at RoundRobinHandle.remove (https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:6:1290129)
at https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:6:1425701
at Map.forEach (<anonymous>)
at removeHandlesForWorker (https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:6:1425682)
at ChildProcess.<anonymous> (https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:6:1427512)
at Object.onceWrapper (https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:6:152909)
at EventEmitter.emit (https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:6:155574)
at finish (https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:6:963993)
at _0x19cf18 (https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:15:148934)
at https://nextjsy1ndyw-jwoi.w-credentialless.staticblitz.com/blitz.6ebe18b1e1db1833797e3e4eb6ff802c375ee61b.js:15:148682
Minimal reproduction example: https://stackblitz.com/edit/nextjs-y1ndyw
I have used next-remote-refresh
successfully in the past for my homepage project, but suddenly the error occurs.
While I was not able to find out which change in my project did introduce this problem, I think I figured out the underlying problem:
Here, next-remote-refresh
creates a websocket server and immediately tries to read the address from it:
https://github.com/souporserious/next-remote-refresh/blob/f934f4883d2a0794b96ba37e23cc88e50cd9e624/index.js#L9
It is using WebSocketServer.address()
of the package ws
, which is invoking http.Server.address()
from Node.js:
github.com/websockets/ws/lib/websocket-server.js#L129-L145
And here's the thing: as is stated in the Node.js net server.address() docs:
server.address() returns null before the 'listening' event has been emitted or after calling server.close().
Solution
Wait for the listening
event before reading the port from the server.
I created a patch with pnpm which I successfully apply in my project, see below. It makes the "withConfig" function async unfortunately, but when using next.config.mjs
and a Node version supporting top-level-await, this is not a huge issue (as you can see here: github.com/pkerschbaum/pkerschbaum-homepage/packages/apps/web/next.config.js#L50).
diff --git a/index.d.ts b/index.d.ts
index 292d05f918836563e1e5d671ae3046a0b257a1a0..943d1eaca68dcb4e9471e8d2e35aeaef2afa1374 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -6,4 +6,4 @@ interface PluginOptions {
signal?: AbortSignal
}
-export default function plugin (options: PluginOptions): (nextConfig: NextConfig) => NextConfig
+export default function plugin (options: PluginOptions): (nextConfig: NextConfig) => Promise<NextConfig>
diff --git a/index.js b/index.js
index 811c02988758a45975eec5352689b8496974368a..ca557ed5a15eef0e6c1d91ef6651300bd20499ae 100644
--- a/index.js
+++ b/index.js
@@ -3,10 +3,15 @@ const createServer = require('./server')
module.exports = function plugin(options) {
let port
- return function withConfig(nextConfig = {}) {
+ return async function withConfig(nextConfig = {}) {
if (process.env.NODE_ENV !== 'production') {
+ const server = createServer(options)
+ await new Promise((resolve, reject) => {
+ server.on('listening', resolve)
+ server.on('error', reject)
+ })
if (port === undefined) {
- ({ port } = createServer(options).address())
+ ({ port } = server.address())
}
if (nextConfig.env === undefined) {