What it means
Before a Lambda can connect to anything by name, it has to turn that name into an address. That lookup goes to the VPC's resolver when the function is VPC-attached, and to AWS-managed DNS when it is not. ENOTFOUND is the resolver answering definitively: no such name.
The word definitively is what makes this error more tractable than its neighbours. Something answered. The function reached a resolver, the resolver looked, and it reported the name does not exist. That eliminates a large family of connectivity problems in one step — routes, NAT gateways and security groups are all working well enough to reach DNS, so whatever is wrong is about the name rather than about the path.
For a VPC-attached function the usual culprit is scope rather than spelling. Private hosted zones answer only for the VPCs they are associated with, so an internal hostname that resolves perfectly from a bastion host, a CI runner, or another account's workload returns NXDOMAIN from a Lambda in a VPC the zone was never attached to. Nothing about the name is wrong; the function is simply asking a resolver that has never been told about it.
The other recurring case is a VPC with enableDnsSupport switched off. A default VPC has it on, and a hand-built or older Terraform-managed one often does not. The symptom is total rather than selective: every name fails, including public ones, because there is no resolver at the .2 address for the function to consult at all. That distinction — one name failing versus every name failing — is usually visible in the logs of a single invocation, and it separates a zone association problem from a VPC attribute problem before you touch anything.
CloudWatch’s Errors metric: Whether CloudWatch’s Errors metric counts this depends on whether the error escaped your handler, which the log line alone does not settle. Counted only when the error escapes the handler. LogStitch classified the example below from its log level rather than an extracted error type, so it reports the failure as caught — read your own stack to see whether anything handled it.
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
8 raw lines, in the order CloudWatch delivered them.
LogStitch After
The same lines, grouped into the invocation they belong to.
Error4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24
- 08:14:02.118PLAT
START RequestId: 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24 Version: $LATEST
- 08:14:02.122INFO
INFO Syncing inventory from internal catalog
- 08:14:02.130DEBUG
DEBUG Resolving api.internal.acme.io
- 08:14:02.148ERROR
ERROR Error: getaddrinfo ENOTFOUND api.internal.acme.io at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) at fetchCatalog (/var/task/src/catalog.js:22:17)3 lines - 08:14:02.402PLAT
END RequestId: 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24
- 08:14:02.402PLAT
REPORT RequestId: 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24 Duration: 284.11 ms Billed Duration: 285 ms Memory Size: 512 MB Max Memory Used: 108 MB Status: error
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
Read the hostname in the message and ask whether it is public or private. A public name that fails to resolve points at DNS configuration in the VPC; a private name — an internal service, a Route 53 private hosted zone entry, an RDS endpoint — points at the zone not being associated with the VPC the function runs in.
Distinguish it from its neighbours. ENOTFOUND is a definitive negative answer: something answered and said the name does not exist. EAI_AGAIN is a timeout talking to the resolver, which is a different failure with a different fix. And ECONNREFUSED means the name resolved fine and the connection was refused, so DNS was never the problem.
Causes, most likely first
The VPC has DNS resolution or DNS hostnames disabled
Check enableDnsSupport and enableDnsHostnames on the VPC. Both default to true on a default VPC and are commonly false on one built by hand or by an older module. With resolution disabled there is no resolver at the .2 address for the function to use, and every lookup fails the same way.
A private hosted zone is not associated with this VPC
If the name is internal, check which VPCs the Route 53 private hosted zone is associated with. A zone serves only the VPCs it is attached to, so a name that resolves from a bastion in one VPC returns NXDOMAIN from a Lambda in another.
The hostname is wrong, stale, or region-specific
Compare the name against the resource it should point at. A service endpoint carrying the wrong region, an RDS instance that was replaced, or an environment variable holding a hostname from another account all produce a genuine NXDOMAIN — the name really is gone.
The function is trying to reach localhost or a container-local name
Look for localhost, 127.0.0.1, or a Docker Compose service name in the hostname. Code moved from a container environment often keeps those, and inside a Lambda execution environment there is nothing else on the loopback address to answer.
Fixes
Turn on DNS support and hostnames for the VPC
Both attributes must be enabled for the VPC resolver to answer, and a Lambda in a VPC has nothing else to ask. This is the first thing to check when every lookup fails rather than one particular name.
bashaws ec2 describe-vpc-attribute --vpc-id vpc-0abc --attribute enableDnsSupport
aws ec2 describe-vpc-attribute --vpc-id vpc-0abc --attribute enableDnsHostnames
aws ec2 modify-vpc-attribute --vpc-id vpc-0abc --enable-dns-support
aws ec2 modify-vpc-attribute --vpc-id vpc-0abc --enable-dns-hostnames
Associate the private hosted zone with the function's VPC
A private zone answers only for VPCs it is associated with. Adding the association makes internal names resolvable from the function's subnets without changing any code.
bashaws route53 associate-vpc-with-hosted-zone \
--hosted-zone-id Z0123456789ABCDEFGHIJ \
--vpc VPCRegion=us-east-1,VPCId=vpc-0abc
Resolve the name from inside the function to see what it actually gets
A one-off diagnostic beats guessing. Resolving from inside the execution environment shows you the answer the function receives, which is frequently not the answer you get from your laptop.
jsimport { Resolver } from "node:dns/promises";
export const handler = async () => {
const resolver = new Resolver();
try {
return { addresses: await resolver.resolve4(process.env.TARGET_HOST) };
} catch (error) {
// code is ENOTFOUND, EAI_AGAIN, ESERVFAIL — each means something different
return { failed: error.code, host: process.env.TARGET_HOST };
}
};
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 — Troubleshoot networking issues
- Amazon VPC User Guide — DNS attributes for your VPC
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.