What it means
A Lambda handler configuration is two pieces of information joined by a dot: which file to load, and which exported name to call inside it. Runtime.HandlerNotFound means the first half succeeded and the second half did not. The file was located and executed; the name was not found on what it exported.
That distinction is genuinely useful, because it eliminates a whole class of suspects immediately. If the deployment package were missing, or the path wrong, you would be looking at Runtime.ImportModuleError instead. Getting HandlerNotFound proves the artifact contains the file, that the file parsed, and that it ran to completion. The problem is narrower than it feels: something about the export.
In practice it is nearly always one of two things. The names disagree — usually because an export was renamed and the deployment configuration was not, or because a bundler emits to a different filename than the source. Or the module systems disagree: a file using ESM export syntax loaded by the CommonJS loader does not present handler where the runtime looks, and the mirror-image mismatch behaves the same way. The .mjs and .cjs extensions and the type field in package.json are what decide which loader runs, and they are easy to get subtly wrong in a monorepo where one package sets "type": "module" and another does not.
Because this is an init failure, its blast radius is total and its logs are sparse. Every invocation on every container fails the same way, none of your code runs, and there is nothing in the log but the runtime's own complaint. The one line that matters is the one naming file.export, and matching it against what the artifact actually exports resolves it almost every time.
CloudWatch’s Errors metric: The error escaped your handler, so Lambda reports the invocation as failed and CloudWatch’s Errors metric counts it. LogStitch classified the invocation above as uncaught.
What it looks like in CloudWatch
This is the shape the failure arrives in: the lines of one invocation scattered among everything else the log group received at the same moment.
CloudWatch Logs Before
5 raw lines, in the order CloudWatch delivered them.
LogStitch After
The same lines, grouped into the invocation they belong to.
ErrorCold start · 277ms inita71b3e08-9d42-4c17-b60a-5f2c8d1e7a93
- 07:03:44.211PLAT
INIT_START Runtime Version: nodejs:22.v14 Runtime Version ARN: arn:aws:lambda:us-east-1::runtime:d1e0a1c1f0e0
- 07:03:44.488
2026-08-29T07:03:44.488Z undefined ERROR Uncaught Exception {"errorType":"Runtime.HandlerNotFound","errorMessage":"index.handler is undefined or not exported","stack":["Runtime.HandlerNotFound: index.handler is undefined or not exported"]} - 07:03:44.602PLAT
START RequestId: a71b3e08-9d42-4c17-b60a-5f2c8d1e7a93 Version: $LATEST
- 07:03:44.604PLAT
END RequestId: a71b3e08-9d42-4c17-b60a-5f2c8d1e7a93
- 07:03:44.604PLAT
REPORT RequestId: a71b3e08-9d42-4c17-b60a-5f2c8d1e7a93 Duration: 2.88 ms Billed Duration: 3 ms Memory Size: 256 MB Max Memory Used: 67 MB Init Duration: 277.44 ms Status: error Error Type: Runtime.HandlerNotFound
The panel on the right is generated by running the excerpt on the left through the same parser that powers the free web stitcher — it is what the tool actually produces for this input, not an illustration of it.
How to confirm it from the logs
The message names exactly what Lambda looked for, in file.export form. index.handler means it loaded index.js (or .mjs/.cjs) and looked for an export called handler. That the error mentions the file at all is the important signal: if the file were missing you would get Runtime.ImportModuleError instead. Getting this error proves the file was found.
Like every init failure it appears before any of your own logging and repeats identically on every invocation. Check the REPORT line for Error Type: Runtime.HandlerNotFound where the runtime writes it.
Causes, most likely first
The handler configuration and the exported name disagree
Compare the configured handler string with the export in the file. The string is file.export, and both halves must match exactly, including case. Renaming an export without updating the deployment configuration is the usual route in.
The module system does not match the export syntax
Check whether the file uses export const handler or exports.handler, and what the package declares. A .js file using ESM export syntax in a package without "type": "module" will not present the export where the CommonJS loader looks for it, and the reverse mismatch fails the same way.
The export is defined but not actually assigned
Read the file for a conditional or late assignment to the export. module.exports replaced wholesale after exports.handler was set, or an export assigned inside an if or a callback, leaves the name absent at the moment Lambda reads it.
The bundler emitted a different export shape
Inspect the built file rather than the source. Bundlers can wrap output in a way that puts the export somewhere the runtime does not look — under default rather than at the top level, for instance — so the source is correct and the artifact is not.
Fixes
Make the handler string match the export exactly
The handler is path/to/file.exportName, without the file extension. If the file sits in a subdirectory of the deployment package, the directory belongs in the string too.
yamlResources:
ApiFunction:
Type: AWS::Serverless::Function
Properties:
# src/handlers/orders.js exporting `handler`
Handler: src/handlers/orders.handler
Runtime: nodejs22.x
Use the export syntax that matches your module type
Pick one module system and be consistent. The file extension and the type field in package.json together decide which loader runs, and the export must be visible to that loader.
js// ESM — file is .mjs, or package.json has "type": "module"
export const handler = async (event) => {
return { statusCode: 200 };
};
// CommonJS — file is .cjs, or package.json has no "type": "module"
// exports.handler = async (event) => ({ statusCode: 200 });
Check the built artifact, not the source
When a bundler is involved, confirm the export survived the build. Loading the emitted file and printing its keys settles it in one command.
bashnode -e "import('./dist/index.js').then(m => console.log(Object.keys(m)))"
Also seen as
The same underlying failure, worded differently by a different runtime, SDK version, or logging layer. All of these land here — there is no separate page for each phrasing.
Related errors
Errors that show up alongside this one, or that people mistake for it.
References
- AWS Lambda Developer Guide — Define Lambda function handler in Node.js
- AWS Lambda Developer Guide — Troubleshoot deployment issues
LogStitch finds this automatically, across every invocation in your account.
Paste a log excerpt into the free web stitcher and see it grouped, classified, and measured in your browser — nothing is uploaded. Or run the Mac app against your own AWS profiles and get the same view over every function you own.