Error: connect ETIMEDOUT 10.0.2.15:443

Short answer

A TCP connection attempt got no answer at all and gave up. On Lambda this is overwhelmingly a networking topology problem: the function is attached to a VPC and has no route to the address it is calling — no NAT gateway, no VPC endpoint, or a security group that drops the traffic.

What it means

By default a Lambda function runs outside your VPC, on AWS-managed networking with unrestricted outbound internet access. Attaching it to a VPC changes that completely: the function gets an elastic network interface in your subnets, and from that moment its connectivity is exactly what your routing tables, NAT configuration and security groups permit — no more.

ETIMEDOUT is what the resulting dead end looks like. It is not a refusal. A refused connection produces ECONNREFUSED almost instantly, because something answered and declined. A timeout means the packets went out and nothing ever came back, which is the signature of a missing route or a security group silently dropping traffic. That silence is why the error is so uninformative about its own cause, and why the address in the message is the most useful thing in it.

The detail that catches almost everyone is that AWS service endpoints are public addresses. It is natural to assume that a function calling S3, DynamoDB or Secrets Manager stays "inside AWS" and needs no internet route. It does not: without a NAT gateway or a VPC endpoint for that specific service, those calls leave the subnet and go nowhere. And because each interface endpoint covers one service, a function can reach S3 through a gateway endpoint perfectly while timing out on Secrets Manager, which looks like a bug in one particular call rather than a topology gap.

The last thing worth knowing is that you often will not see this error at all. Most SDK and HTTP clients default to connect timeouts far longer than a typical Lambda's own timeout, so the function is killed before the connection gives up — and you get Task timed out after N seconds with no mention of networking anywhere. Setting a short connect timeout is therefore not just good practice but a diagnostic technique: it converts a silent timeout into a logged error naming the address that could not be reached.

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 when the error escapes the handler; code that catches it and returns a degraded response is not counted. Most commonly the Lambda's own timeout arrives first and the invocation is recorded as a timeout instead. LogStitch classified the example below from its log level rather than an extracted error type.

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-29T10:31:12.008Z START RequestId: 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75 Version: $LATEST
2026-08-29T10:31:12.012Z 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75 INFO Publishing daily digest recipients=1841
2026-08-29T10:31:12.118Z 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75 INFO Resolved api.sendgrid.com to 167.89.118.32
2026-08-29T10:31:22.204Z 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75 ERROR Error: connect ETIMEDOUT 167.89.118.32:443
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
at sendDigest (/var/task/src/mailer.js:37:11)
2026-08-29T10:31:22.402Z END RequestId: 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75
2026-08-29T10:31:22.402Z REPORT RequestId: 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75 Duration: 10394.11 ms Billed Duration: 10395 ms Memory Size: 512 MB Max Memory Used: 114 MB Status: error

LogStitch After

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

Error3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75dur 10.39sbilled 10.39smem 114/512MBlogs 6
  1. 10:31:12.008PLAT
    START RequestId: 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75 Version: $LATEST
  2. 10:31:12.012INFO
    INFO	Publishing daily digest recipients=1841
  3. 10:31:12.118INFO
    INFO	Resolved api.sendgrid.com to 167.89.118.32
  4. 10:31:22.204ERROR
    ERROR	Error: connect ETIMEDOUT 167.89.118.32:443
        at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
        at sendDigest (/var/task/src/mailer.js:37:11)
    3 lines
  5. 10:31:22.402PLAT
    END RequestId: 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75
  6. 10:31:22.402PLAT
    REPORT RequestId: 3a8d4f10-5c27-4e69-b013-8f2a6c9d1e75	Duration: 10394.11 ms	Billed Duration: 10395 ms	Memory Size: 512 MB	Max Memory Used: 114 MB	Status: error
Error
Status
10.39s
Duration
10.39s
Billed
114/512MB
Memory
78%
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

The distinguishing feature is silence, not refusal. ECONNREFUSED means something answered and said no; ETIMEDOUT means nothing answered at all, which is what a dropped packet looks like. The address in the message tells you where it was going — a private 10.x or 172.16-31.x address is inside your VPC, anything else is being routed out of it.

Check the elapsed time before the error against your client's connect timeout. If the two match, the connection attempt ran its full course without a response. And check whether the invocation reached Task timed out first: with a default SDK timeout longer than the function's, Lambda's clock usually expires before the connection does, and you get a timeout with no error at all.

Causes, most likely first

1

The function is in a VPC with no route to the internet

How to confirm

Check whether the function has a VPC configuration and whether its subnets are private with a NAT gateway. A Lambda in a VPC has no public IP; a private subnet without a NAT route cannot reach any public endpoint, and the failure is silence rather than an error. This is the single most common cause.

2

There is no VPC endpoint for the AWS service being called

How to confirm

Look at which AWS service the call targets. AWS service endpoints are public addresses, so a VPC-attached function needs either a NAT route or an interface/gateway endpoint for that specific service. S3 and DynamoDB use gateway endpoints; most others need interface endpoints, each configured individually.

3

A security group or NACL is dropping the traffic

How to confirm

Check the function's security group egress rules and the destination's ingress rules. A security group that does not allow outbound on the port, or a target whose group does not allow inbound from the function's, drops packets silently — which produces a timeout rather than a rejection.

4

The destination is unreachable or the address is wrong

How to confirm

Confirm the address in the message is the one you intend. A stale private IP, a hostname resolving to an address in a different VPC, or a database that has moved all produce a connect timeout with no other symptom.

5

The function was recently attached to a VPC

How to confirm

Check whether the VPC configuration changed around the time the failures started. Adding a VPC configuration removes the function's default internet access entirely, so calls that worked for months begin timing out immediately after a change that looks unrelated.

Fixes

Fix 1

Give private subnets a NAT route, or use VPC endpoints

A VPC-attached function reaches public endpoints only through a NAT gateway in a public subnet. For AWS services specifically, a VPC endpoint is cheaper and keeps the traffic off the internet entirely — a gateway endpoint for S3 and DynamoDB, an interface endpoint for the rest.

yaml# Gateway endpoint — no hourly charge, routes S3 traffic inside the VPC.
S3Endpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    VpcId: !Ref Vpc
    ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
    VpcEndpointType: Gateway
    RouteTableIds: [!Ref PrivateRouteTable]
Fix 2

Set connect timeouts short enough to produce an error

A connect timeout below the function's own timeout converts a silent Lambda timeout into a logged ETIMEDOUT naming the address. That single change is often what makes the problem diagnosable at all.

jsimport { NodeHttpHandler } from "@smithy/node-http-handler";

const client = new S3Client({
  requestHandler: new NodeHttpHandler({
    connectionTimeout: 2000,   // fail fast and name the address
    requestTimeout: 5000
  })
});
Fix 3

Ask whether the function needs a VPC at all

A Lambda only needs a VPC configuration to reach private resources — an RDS instance, an ElastiCache cluster, an internal service. If it only calls AWS service endpoints and public APIs, removing the VPC configuration restores default internet access and removes an entire class of failure.

bash# Confirm what the function is attached to before changing anything.
aws lambda get-function-configuration \
  --function-name orders-fn \
  --query 'VpcConfig'
Fix 4

Check the security groups on both ends

Egress from the function's security group and ingress on the destination's must both permit the port. VPC Reachability Analyzer tests the whole path and names the component that blocks it, which is faster than reading rules by hand.

bashaws ec2 create-network-insights-path \
  --source "$LAMBDA_ENI_ID" \
  --destination "$RDS_ENI_ID" \
  --protocol tcp --destination-port 5432

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.

connect ETIMEDOUTTimeoutError: Connection timed out after 120000mssocket.timeout: The read operation timed outError: Socket timed out without establishing a connectionlambda vpc cannot reach internetlambda etimedout s3

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.