How to solve "Cannot redefine property: handler" on AWS Lambda
Tags: aws, tutorial, debugging, javascript
Published: 2022-03-06 17:40:58

Hey, there!
Today I spent a good few hours trying to solve a problem on AWS Lambda and given how deep the problem was, I thought it would be a good idea to share the solution.

## ✏️ The Context

I am using [Jovo Framework](https://www.jovo.tech/) to develop a voice app for both Alexa and Google Assistant and one of their guides teach [how to deploy the code to an AWS Lambda](https://www.jovo.tech/marketplace/server-lambda) function using [serverless CLI](https://www.serverless.com/framework/docs/providers/aws/guide/functions).  

## 🛑 The Problem

After setting everything up and having the code deployed successfully, when trying to run the lambda function the following error was popping up in the cloud logs

```bash
{
  "errorType": "TypeError",
  "errorMessage": "Cannot redefine property: handler",
  "trace": [
    "TypeError: Cannot redefine property: handler",
    "    at Function.defineProperty (<anonymous>)",
    "    at defineProperty (/opt/otel-extension/node_modules/shimmer/index.js:14:10)",
    "    at AwsLambdaInstrumentation.wrap [as _wrap] (/opt/otel-extension/node_modules/shimmer/index.js:56:3)",
    "    at InstrumentationNodeModuleFile.patch (/opt/otel-extension/node_modules/@opentelemetry/instrumentation-aws-lambda/src/instrumentation.ts:124:20)",
    "    at AwsLambdaInstrumentation._onRequire (/opt/otel-extension/node_modules/@opentelemetry/instrumentation/src/platform/node/instrumentation.ts:109:23)",
    "    at /opt/otel-extension/node_modules/@opentelemetry/instrumentation/src/platform/node/instrumentation.ts:143:25",
    "    at Module.Hook._require.Module.require (/opt/otel-extension/node_modules/require-in-the-middle/index.js:154:32)",
    "    at Module.Hook._require.Module.require (/opt/otel-extension/node_modules/require-in-the-middle/index.js:80:39)",
    "    at Module.Hook._require.Module.require (/opt/otel-extension/node_modules/require-in-the-middle/index.js:80:39)",
    "    at Module.Hook._require.Module.require (/opt/otel-extension/node_modules/require-in-the-middle/index.js:80:39)"
  ]
}
```


!["Cannot redefine property: handler" error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ynsjne58y4s700qmhvc.png)
_Note: in the image above the error shows as "Cannot redefine property: myHandler" because I've changed the function name for debugging purposes._

The easiest way to reproduce the error was using the [Test](https://docs.aws.amazon.com/lambda/latest/dg/getting-started-create-function.html) feature from AWS Lambda with an Alexa Start Session template.

![Testing AWS Lambda](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fdldjogssm5wz2coy3d.png)
 

## 🤕 The culprit

It took me a while to notice that, according to the stack trace, the error was happening in a dependency of `otel-extension`: shimmer

```
at defineProperty (/opt/otel-extension/node_modules/shimmer/index.js:14:10)
```

[Shimmer](https://github.com/othiym23/shimmer) is a monkeypatching library that a [telemetry extension for AWS Lambda](https://github.com/aws-observability/aws-otel-lambda) was using.

According to this [issue](https://github.com/aws-observability/aws-otel-lambda/issues/99), the problem is using [esbuild](https://esbuild.github.io/t) bundle generator - that [Jovo uses](https://github.com/jovotech/jovo-framework/blob/ec3b2b45281280322056685591f5f3fa8ebf88fc/examples/typescript/basic/package.json#L12) - with this telemetry extension.

The responsible for actually adding this telemetry extensions as an AWS Lambda layer was [serverless cli](https://github.com/serverless/serverless/blob/main/package.json#L25).

## 🔧 The solution

As suggested in both [opentelemetry-js-contrib](https://github.com/open-telemetry/opentelemetry-js-contrib/issues/647) and [aws-otel-lambda](https://github.com/aws-observability/aws-otel-lambda/issues/99) issues, the solution is changing ES6 `export` to CommonJS `module.exports`.

Instead of

```js
export const handler = () => {
  // removed for brevity
};
```

Do this

```js
const handler = () => {
  // removed for brevity
};
module.exports = { handler }
```

But, since I'm using [Jovo with AWS Lambda](https://www.jovo.tech/marketplace/server-lambda), I actually had to change the `app.prod.ts` file (where `prod` can be other stage name you defined while running `jovo new:stage <stage>`).

```js
// app.prod.ts
import { app } from './app'
import { handler } from './server.lambda'

app.configure({
  //...
})

module.exports = { handler }
```

## 🙏 The Conclusion

I was sure that I was doing something wrong in some configuration step, since this setup involves several services: Jovo, Serveless, AWS Lambda and AWS API Gateway.  
It was kinda relieving to know that the problem was actually a compatibility issue between esbuild and open-telemetry.  
Nonetheless, I was able to learn a lot about these services as usual with any deep debugging problem we have to deal as developers.