serverless-offline lambdas break in node20.19 and node22 due to require(esm)
Bug Report
Current Behavior
When running serverless-offline in Node 22, and now Node 20.19, there is a startup error.
import { Server } from "@hapi/hapi"
^^^^^^
SyntaxError: Named export 'Server' not found. The requested module '@hapi/hapi' is a CommonJS module, which may not support all module.exports as named exports.
Sample Code
Running a basic serverless setup with a lambda function should trigger the issue.
- file: serverless.yml
service: my-service
plugins:
- serverless-offline
provider:
runtime: nodejs18.x
stage: dev
functions:
hello:
events:
- http:
method: get
path: hello
handler: handler.hello
- file: handler.js
"use strict"
const { stringify } = JSON
exports.hello = async function hello() {
return {
body: stringify({ foo: "bar" }),
statusCode: 200,
}
}
Expected behavior/code
Environment
-
serverlessversion: 3.40 -
serverless-offlineversion: 14.4 -
node.jsversion: 20.19 or 22.x -
OS: macOS
Possible Solution
import hapi from "@hapi/hapi"
const { Server } = hapi;
Also, fs-extra, jsonschema, aws-lambda-ric/UserFunction.js. and possibly other commonjs packages that are not triggered by our code.
Additional context/Screenshots
The issue can be avoided by not upgrading node versions, or potentially engaging available node experimental flags, but updating the code to follow the now prescribed import-of-commonjs syntax seems appropriate.
This appears to have been a serverless framework issue and was fixed with v3.40: https://github.com/serverless/serverless/issues/12936#issuecomment-2549292037
EDIT: the exception looks different. if serverless v3.40 does not fix the issue, you might have to double check the versions of serverless, serverless offline, and node.js.
We are up to do date with serverless 3.40. These errors are only appearing in the serverless-offline code, creating a patch-package patch for each commonjs import has resolved the issue for us for now.
We are having the same issue since Node v20.19.0 was released last week. We have isolated the issue to the serverless-offline plugin
Environment: darwin, node 20.19.0, framework 3.40.0 (local), plugin 7.2.3, SDK 4.5.1
Docs: docs.serverless.com
Support: forum.serverless.com
Bugs: github.com/serverless/serverless/issues
Error:
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.
at ModuleJobSync.runSync (node:internal/modules/esm/module_job:384:13)
at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:323:47)
at loadESMFromCJS (node:internal/modules/cjs/loader:1371:24)
at Module._compile (node:internal/modules/cjs/loader:1511:5)
at Module._extensions..js (node:internal/modules/cjs/loader:1572:16)
at Module.load (node:internal/modules/cjs/loader:1275:32)
at Module._load (node:internal/modules/cjs/loader:1096:12)
at Module.require (node:internal/modules/cjs/loader:1298:19)
at require (node:internal/modules/helpers:182:18)
at module.exports (/Users/kylecombs/code/eva-personnel-service/node_modules/serverless/lib/utils/require-with-import-fallback.js:5:17)
at PluginManager.requireServicePlugin (/Users/kylecombs/code/eva-personnel-service/node_modules/serverless/lib/classes/plugin-manager.js:171:14)
at PluginManager.resolveServicePlugins (/Users/kylecombs/code/eva-personnel-service/node_modules/serverless/lib/classes/plugin-manager.js:198:29)
at async PluginManager.loadAllPlugins (/Users/kylecombs/code/eva-personnel-service/node_modules/serverless/lib/classes/plugin-manager.js:136:36)
at async Serverless.init (/Users/kylecombs/code/eva-personnel-service/node_modules/serverless/lib/serverless.js:146:5)
at async /Users/kylecombs/code/eva-personnel-service/node_modules/serverless/scripts/serverless.js:601:7
We have tried various versions of Serverless up to 3.40.0 and various versions of serverless-offline, but the only fix seems to be keeping Node.js at v20.18.3.
Would love if there was a fix so we can continue to upgrade Node.js versions and use serverless-offline
npm list --depth=1 | grep serverless
├─┬ [email protected]
├─┬ [email protected]
├─┬ [email protected]
├─┬ [email protected]
│ └── [email protected] deduped
├─┬ [email protected]
│ └── [email protected]
├── [email protected]
├─┬ [email protected]
├─┬ [email protected]
│ ├── [email protected] deduped
├─┬ [email protected]
│ └── [email protected] deduped
├─┬ [email protected]
├─┬ [email protected]
│ ├── @serverless/[email protected]
│ ├── @serverless/[email protected]
│ ├── @serverless/[email protected]
UPDATE: - I am able to run serverless-offline in Node 22 or Node 20.18.x. It appears this is only an issue in 20.19.0
After using this command: export NODE_OPTIONS="--experimental-print-required-tla" we were able to track down the file in our version of serverless-offline (v12.0.4) that was causing the problem, but updating the package just brings a different file and a quick search of the repo has a lot of top-level await in use from what I can see.
This file was located node_modules/serverless-offline/src/index.js
// TODO remove with node.js v16.9+ support
import 'object.hasown/auto'
// install global fetch
// TODO remove `node-fetch` module and use global built-in with node.js v18+ support
if (globalThis.fetch === undefined) {
const { default: fetch, Headers } = await import('node-fetch')
globalThis.fetch = fetch
globalThis.Headers = Headers
}
export { default } from './ServerlessOffline.js'
We were able to resolve the issue by using this command in our environment for the time being, but would love something more long term as it appears that this requirement will soon be released in Node v22 and v23 per the Node.js v20.19.0 release notes.
export NODE_OPTIONS="--no-experimental-require-module"
I can't repro with node v20.19.0, serverless 3.40, and serverless offline 14.4. I don't mind looking into this if someone could create a small repro repository.
This is also happening to me when bumping node to v20.19x, which happens automatically on our CI/CD setup as we use the LTS version for node 20x:
Environment: linux, node 20.19.0, framework 3.33.0 (local), plugin 6.4.0, SDK 4.5.1
Docs: docs.serverless.com
Error:
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.
at ModuleJobSync.runSync (node:internal/modules/esm/module_job:384:13)
at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:323:47)
at loadESMFromCJS (node:internal/modules/cjs/loader:1371:24)
at Module._compile (node:internal/modules/cjs/loader:1511:5)
at Module._extensions..js (node:internal/modules/cjs/loader:1572:16)
at Module.load (node:internal/modules/cjs/loader:1275:32)
at Module._load (node:internal/modules/cjs/loader:1096:12)
at Module.require (node:internal/modules/cjs/loader:1298:19)
at require (node:internal/modules/helpers:182:18)
The only fix was to lock the version in 20.18x. I didn't try the NODE_OPTIONS though.
We had the same issue, Downgrading node version to 20.18 worked for us serverless version: 3.40 serverless-offline version: 11.6.0 node.js version: 20.19 => 20.18.3 OS: Windows