serverless-offline icon indicating copy to clipboard operation
serverless-offline copied to clipboard

serverless-offline lambdas break in node20.19 and node22 due to require(esm)

Open smundro opened this issue 1 year ago • 8 comments

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

  • serverless version: 3.40
  • serverless-offline version: 14.4
  • node.js version: 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.

smundro avatar Mar 14 '25 19:03 smundro

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.

dnalborczyk avatar Mar 18 '25 16:03 dnalborczyk

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.

smundro avatar Mar 18 '25 17:03 smundro

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]

kyle4eva avatar Mar 19 '25 17:03 kyle4eva

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

kyle4eva avatar Mar 19 '25 18:03 kyle4eva

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"

kyle4eva avatar Mar 19 '25 19:03 kyle4eva

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.

dnalborczyk avatar Mar 20 '25 00:03 dnalborczyk

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.

carlovsk avatar Mar 28 '25 19:03 carlovsk

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

tawfiek avatar Oct 08 '25 10:10 tawfiek