Issue with deploying macOS-built node module

One of my node modules (web3) has a dependency on scrypt. When I deploy and invoke it, I get an error:

{
    "errorMessage": "/var/task/node_modules/scrypt/build/Release/scrypt.node: invalid ELF header",
    "errorType": "Error",
    "stackTrace": [
        "Object.Module._extensions..node (module.js:597:18)",
        "Module.load (module.js:487:32)",
        "tryModuleLoad (module.js:446:12)",
        "Function.Module._load (module.js:438:3)",
        "Module.require (module.js:497:17)",
        "require (internal/module.js:20:19)",
        "Object.<anonymous> (/var/task/node_modules/scrypt/index.js:3:20)",
        "Module._compile (module.js:570:32)",
        "Object.Module._extensions..js (module.js:579:10)"
    ]
}

I did some research and it looks like it’s due to the fact that I npm installed it on a macOS machine (my development machine) and deployed it to Lambda, which is Unix-based. More info here:

Is there a way around this issue? I could try to build it on Linux and copy-paste the module files into my dev environment, but then my local environment would not work properly.

I came across this thread that has a workaround, but not a great solution:

https://github.com/serverless/serverless/issues/308

Have there been any updates in this area?

I had the same problem with the node_expat module. After failing to come up with an elegant solution I decided to keep both versions (mac compiled and linux compiled) in my node_modules directory (e.g. node_expat.mac and node_expat.linux). Before deploying my function I ran a script to rename the linux version to the proper filename and deployed.

It’s not pretty but it worked.

If you’re using native NPM’s you need to provide a version compiled for Lambda (i.e. using the correct AWS AMI) when you upload your node_modules folder. If you don’t it won’t run. It might worth looking into using CI/CD to deploy or adding a deploy script inside your package.json so that you always have a consistent deployment experience.

1 Like

Just to add to @buggy’s answer, you can use https://github.com/lambci/docker-lambda to run a local container that is the same as the remote environment.

use bcrypt.js instead of bcrypt