Skip to content

ESM handler resolution #782

@zbrydon

Description

@zbrydon

Expected Behavior

dist/handler.js should not be present in the published package for Node.js runtimes ≥ 14, as it causes the Lambda bootstrap to resolve the CJS entry point instead of dist/handler.mjs, preventing ESM user modules from loading correctly.

Actual Behavior

dist/handler.js is present in the published dist because tsc compiles src/handler.ts to dist/handler.js and it is never removed by the post-build script (scripts/update_dist_version.sh). When the Lambda bootstrap resolves the handler string node_modules/datadog-lambda-js/dist/handler.handler, it finds handler.js first and loads it as CJS. This causes loadSync_tryRequireSync to be invoked, which does not attempt to load .mjs files, resulting in a failure when the user's module is ESM.

The root cause is in update_dist_version.sh:

cp src/handler.* dist/

This copies src/handler.mjs and src/handler.cjs into dist/, but dist/handler.js produced by tsc from src/handler.ts, is never cleaned up.

Steps to Reproduce the Problem

  1. Create a Lambda function with an ESM bundle (e.g. index.mjs) and a package.json with "type": "module" at the task root.
  2. Set the Lambda handler string to node_modules/datadog-lambda-js/dist/handler.handler (standard Datadog handler redirect).
  3. Deploy and invoke the Lambda on Node.js 18 or above.

Specifications

  • Datadog NPM version: 12.138.0 (latest at time of writing dist/handler.js confirmed present)
  • Node version: 18.x and above (also reproduced on 24.x)

Stacktrace

{
  "errorType": "Error",
  "errorMessage": "require() of ES Module /var/task/index.mjs from /var/task/node_modules/datadog-lambda-js/dist/runtime/user-function.js not supported.",
  "stack": [
    "Error [ERR_REQUIRE_ESM]: require() of ES Module /var/task/index.mjs ...",
    "    at _tryRequireSync (.../datadog-lambda-js/dist/runtime/user-function.js)",
    "    at _loadUserAppSync (.../datadog-lambda-js/dist/runtime/user-function.js)",
    "    at loadSync (.../datadog-lambda-js/dist/runtime/user-function.js)",
    "    at Object.<anonymous> (.../datadog-lambda-js/dist/handler.js)"
  ]
}

Workaround: Manually delete dist/handler.js after npm install in the Dockerfile (as documented in #295). This was flagged in 2022 with a promise to remove the file after Node 12 EOL, but it remains in the package today.

Suggested fix: Add rm dist/handler.js to update_dist_version.sh after the cp src/handler.* dist/ step, since the .mjs and .cjs variants now fully cover all supported runtimes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions