Error: getaddrinfo ENOTFOUND api.internal.acme.io

Short answer

DNS resolution returned "no such host". The name genuinely does not resolve from where the function is running — which, for a VPC-attached Lambda, frequently means the name resolves fine everywhere else and not from inside your subnets.

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.

Depends

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.

2026-08-30T08:14:02.118Z START RequestId: 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24 Version: $LATEST
2026-08-30T08:14:02.122Z 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24 INFO Syncing inventory from internal catalog
2026-08-30T08:14:02.130Z 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24 DEBUG Resolving api.internal.acme.io
2026-08-30T08:14:02.148Z 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24 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)
2026-08-30T08:14:02.402Z END RequestId: 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24
2026-08-30T08:14:02.402Z 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

LogStitch After

The same lines, grouped into the invocation they belong to.

Error4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24dur 284msbilled 285msmem 108/512MBlogs 6
  1. 08:14:02.118PLAT
    START RequestId: 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24 Version: $LATEST
  2. 08:14:02.122INFO
    INFO	Syncing inventory from internal catalog
  3. 08:14:02.130DEBUG
    DEBUG	Resolving api.internal.acme.io
  4. 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
  5. 08:14:02.402PLAT
    END RequestId: 4b7e91c3-2a58-4d06-9f71-3c8a1e5b0d24
  6. 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
Error
Status
284ms
Duration
285ms
Billed
108/512MB
Memory
79%
Headroom
No
Cold start
LogLevelError
Error type

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

1

The VPC has DNS resolution or DNS hostnames disabled

How to confirm

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.

2

A private hosted zone is not associated with this VPC

How to confirm

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.

3

The hostname is wrong, stale, or region-specific

How to confirm

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.

4

The function is trying to reach localhost or a container-local name

How to confirm

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

Fix 1

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
Fix 2

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
Fix 3

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.

getaddrinfo ENOTFOUND localhostsocket.gaierror: [Errno -2] Name or service not knownjava.net.UnknownHostExceptionlambda enotfoundlambda cannot resolve hostname

Errors that show up alongside this one, or that people mistake for it.

References

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.