image of lucas amos

Lucas Amos

Cloud Software Engineer

Using nodejs source maps with AWS Lambda

Modern JavaScript bundlers minify and compress code to reduce bundle sizes. While this makes shipping code to end user devices faster it proses problems when debugging errors.

The following example will explain how sourcemaps can be enabled on AWS Lambda functions.


Consider the following file structure

/utils/operands/addition.js
/utils/operands/index.js
/utils/operands/multiplication.js
/utils/index.js
/utils/maths.js
index.js

In utils/operands/multiplication.js there is a function multiply that will throw an error if the two inputs a and b are both zero.

export function multiply(a, b) {
  if ((a === 0) & (b === 0)) {
    throw new Error("values cannot both be zero");
  }
  return a * b;
}


When the lambda function is invoked with the event { "a": 0, "b": 0 }, the following error is returned.

{
  "errorType": "Error",
  "errorMessage": "values cannot both be zero",
  "trace": [
    "Error: values cannot both be zero",
    "  at /var/task/index.js:1:140",
    "  at /var/task/index.js:1:191",
    "  at exports.lambdaHandler [as handler] (/var/task/index.js:1:228)",
    "  at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1147:29)"
  ]
}

This output is totally unhelpful in pinpointing the source of the error. Indeed it appears to be identifying the cause as line 1 of index.js when we know that the error is occurring on line 3 of multiplication.js
To understand why this is the case we can look at the minified code generated by webpack, which when not formatted is one long function on one line.

(() => {
  "use strict";
  exports.lambdaHandler = async function (n) {
    const { a: r, b: t } = n;
    return (function (n, r) {
      return (
        (function (n, r) {
          if ((0 === n) & (0 === r)) throw new Error("values cannot both be zero");
          return n * r;
        })(n, r) -
        (function (n, r) {
          return n + r;
        })(n, r)
      );
    })(r, t);
  };
})();

Sourcemaps

The solution is to generate a sourcemap that enables the Lambda function to reconstruct the original source from the minified code.

In webpack sourcemaps can enabled by adding devtool: "source-map" to webpack.config.js and setting the Lambda function's NODE_OPTIONS=--enable-source-maps environment variable

Cloudwatch log insights

With sourcemap

Once the sourcemap has been enabled we can rerun the Lambda function. The error is now correctly identified as occurring on line 3 of multiplication.js, making debugging much easier.

{
  "errorType": "Error",
  "errorMessage": "values cannot both be zero",
  "trace": [
    "Error: values cannot both be zero",
    "  at multiply (webpack://lambda-node-source-maps/utils/operands/multiplication.js:3:11)",
    "  at calculation (webpack://lambda-node-source-maps/utils/maths.js:4:10)",
    "  at async (webpack://lambda-node-source-maps/index.js:6:10)",
    "  at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1147:29)"
  ]
}

All of the code for this example is available on GitHub