invalid ELF header when using the nodejs ref module on AWS Lambda
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
invalid ELF header on AWS Lambda almost always means a native Node.js addon was built for the wrong operating system, C library, or CPU architecture. With the ref family of modules, that usually happens when node_modules was installed on macOS or Windows and then zipped into Lambda unchanged. Lambda is expecting a Linux shared object compatible with the function runtime, but the package contains something else.
Why This Error Happens
Modules such as ref, ffi, or newer N-API variants may include compiled binary addons. Those addons are not plain JavaScript. They are native shared libraries loaded by Node.js.
When the package is built on the wrong platform, Lambda cannot load the binary. Typical mismatches include:
- macOS build artifacts deployed to Linux
- Windows build artifacts deployed to Linux
- '
arm64binaries deployed tox86_64Lambda, or the reverse' - binaries compiled against the wrong Node.js ABI or runtime version
The phrase ELF header is the clue. ELF is the Linux executable and shared-library format. If the file is actually a Mach-O binary from macOS or a PE file from Windows, Lambda rejects it immediately.
The Core Fix: Build in a Lambda-Compatible Environment
The safe solution is to install and compile dependencies in an environment that matches the Lambda runtime.
If your function uses the Node.js 20 Lambda runtime on x86_64, build the native addon in a Linux environment compatible with that runtime and that architecture.
One practical way is to build inside the official Lambda base image:
If the function will run on arm64, use an arm64-compatible build flow instead of reusing x86_64 artifacts.
Package the Right node_modules
After building in the correct environment, deploy the resulting node_modules directory together with your handler code.
A minimal Lambda layout might be:
And the handler itself can stay simple:
The real difficulty is not the JavaScript API. It is making sure the native dependency was compiled for Lambda before you zip or publish the function.
Prefer Maintained N-API Variants When Possible
Older modules such as ref and ffi have a long history and several forks. In many cases, maintained N-API variants such as ref-napi are easier to keep working across Node.js versions because they rely on a more stable native interface.
That does not eliminate the need for correct packaging, but it can reduce runtime-ABI problems compared with older bindings.
Match the Lambda Runtime Exactly
Be careful about more than just Linux versus non-Linux. Native addons can also fail when:
- the local Node.js version differs from the Lambda runtime
- the deployment switched from
x86_64toarm64 - the addon depends on external native libraries not present in the runtime
When diagnosing, confirm all of these:
Those values should line up with the environment used to build the addon and the Lambda runtime where it will execute.
Layers and CI Pipelines
For repeatable builds, use CI or Lambda layers rather than building locally on a laptop and hoping the binary matches. A CI job that runs the build inside the correct container is much more reliable.
That workflow usually looks like:
- install dependencies in a Lambda-compatible container
- run tests in that same environment if possible
- zip the produced artifacts
- deploy the zip or publish a layer
This eliminates the most common source of invalid ELF header: local developer machine artifacts leaking into the deployment package.
Common Pitfalls
The most common mistake is running npm install on macOS or Windows and deploying that node_modules directory directly to Lambda.
Another mistake is fixing the operating system mismatch but ignoring architecture. Lambda functions can run on x86_64 or arm64, and the native addon must match.
Developers also sometimes rebuild the package correctly but then deploy an old zip file that still contains stale binaries.
Finally, do not assume every ffi-style package is equally maintained. If a module is outdated, you may spend time fighting ABI issues that a newer maintained alternative avoids.
Summary
- '
invalid ELF headerusually means the native addon was built for the wrong platform or architecture.' - Build native Node.js dependencies in a Lambda-compatible Linux environment.
- Match the Lambda runtime version and CPU architecture during the build.
- Package the rebuilt
node_modulesdirectory, not artifacts from a local non-Linux machine. - Prefer maintained N-API-based modules when the older package is brittle.

