You've got a Lambda behind API Gateway. It works fine when you hit Test in the console. The moment you call it through the API endpoint, you get Task timed out after 3.00 seconds — or 10, or 29, depending on what you configured. Nothing in CloudWatch except the timing-out log line. No stack trace, no error from your code.
What's actually happening here is the function is running. It's just stuck on a network call that will never complete. And nine times out of ten, it's because your Lambda is attached to a VPC and the subnet it's sitting in has no working route to the internet.
Cause 1: Lambda is in a private subnet with no NAT Gateway route
This is the big one. You attach a Lambda to a VPC because you need to reach RDS, ElastiCache, or some other private resource. AWS puts the ENI in the subnet you chose. If that subnet's route table doesn't have a 0.0.0.0/0 entry pointing at a NAT Gateway, every outbound call to the public internet just... hangs. TCP SYN packets go nowhere. Lambda waits for a SYN-ACK that never arrives, and then the runtime hits the timeout.
Typical real-world trigger: your function calls the Stripe API, or SES, or DynamoDB, or anything with a public endpoint. It works locally. It works in a non-VPC test Lambda. It dies in production because someone attached it to a VPC for database access and forgot the egress path.
Confirm it
Look at the route table associated with your Lambda's subnet. In the console: VPC → Route Tables → find the one with the subnet association → check the routes.
A private subnet with NAT should look like:
Destination Target
10.0.0.0/16 local
0.0.0.0/0 nat-0a1b2c3d4e5f67890
If that second row is missing, or it points at an igw- (internet gateway) instead of a nat-, that's your bug. Internet gateways only work for resources with public IPs. Lambda ENIs don't get public IPs.
The fix
- Create a NAT Gateway in a public subnet (one that has an IGW route). Give it an Elastic IP.
- Edit the route table for your Lambda's private subnet.
- Add route: destination
0.0.0.0/0, target: your new NAT Gateway. - Wait 30-60 seconds. Lambda reuses the ENI, so no cold start needed.
One NAT Gateway per AZ if you care about resilience. Yes, it costs about $32/month plus data processing. Yes, that's annoying. There's no free version of this.
Cause 2: Security group or NACL is blocking egress
Less common, but it bites people who tightened things down. Your Lambda's security group needs outbound rules that allow the traffic. The default SG does this (0.0.0.0/0 on all protocols), but if someone "hardened" it, you might find outbound restricted to 10.0.0.0/16 only.
Network ACLs are the sneakier one. NACLs are stateless. If you have a custom NACL on the subnet and the outbound rule allows port 443 but the inbound ephemeral range (1024-65535) isn't open, return traffic gets dropped. The connection looks like it's hanging, same symptom as no NAT.
Confirm it
Check the Lambda's security group outbound rules:
aws ec2 describe-security-groups \
--group-ids sg-0123456789abcdef0 \
--query 'SecurityGroups[0].IpPermissionsEgress'
Then the NACL on the subnet. If you see DENY rules or a restrictive allow list, that's your culprit.
The fix
- Security group: allow outbound to
0.0.0.0/0on the protocol/port you need. Narrow it later once things work. - NACL: add inbound allow rules for ephemeral ports 1024-65535 from
0.0.0.0/0, and outbound 443 (and 80 if you need it).
Order matters. Fix the SG first, retest, then the NACL. Changing both at once makes it harder to know which one was the problem.
Cause 3: API Gateway's own 29-second limit is hiding a real timeout
API Gateway caps integration requests at 29 seconds. Your Lambda might have a 300-second timeout configured, but if the function takes 35 seconds to finish, API Gateway returns 504 Endpoint request timed out while the Lambda keeps running — and you get billed for it. People misread this as a Lambda timeout and go chasing the wrong thing.
Also: the default Lambda timeout is 3 seconds. If someone forgot to raise it, you'll see exactly three seconds in the logs no matter what your downstream service does.
Confirm it
Look at the actual error message in the API response body. If it's 504 from API Gateway, that's the 29-second wall. If it's Task timed out after N seconds in CloudWatch, that's the Lambda timeout.
The fix
Raise the Lambda timeout to something below 29 seconds if you're going through API Gateway REST/HTTP APIs. If you genuinely need longer, switch to asynchronous invocation (SQS, EventBridge) and have the client poll for results. There's no way around 29 seconds on the synchronous API Gateway path — this is a hard limit, not a soft one.
aws lambda update-function-configuration \
--function-name my-func \
--timeout 25
25 seconds gives you headroom. Don't set it to 29, because the clock on Lambda and the clock on API Gateway don't tick identically and you'll lose the race.
Why console tests pass but API Gateway fails
Quick note on this, because it confuses people. When you hit Test in the Lambda console, the request runs the same code path. The difference is usually the inputs. API Gateway passes a proxy event with headers, query strings, and a body that your handler has to parse. If your code tries to fetch something (an SSM parameter, a secret from Secrets Manager, a Stripe customer) with an ID from the event, and that fetch hangs because of the VPC egress problem, you'll only see it on real invocations. Console tests with hardcoded inputs might skip the network call entirely.
Quick reference
| Symptom | Likely cause | Fix |
|---|---|---|
| Works in console, hangs via API, no error logs | No NAT Gateway route in private subnet | Add 0.0.0.0/0 → NAT Gateway to subnet route table |
| Hangs only after SG/NACL changes | Outbound blocked by SG or NACL | Allow 443 outbound; open ephemeral 1024-65535 inbound on NACL |
504 from API Gateway at ~29s | API Gateway integration limit | Keep Lambda under 29s or go async |
Task timed out after 3.00 seconds | Default Lambda timeout | aws lambda update-function-configuration --timeout 25 |
| Works in one AZ, fails in another | NAT Gateway missing in that AZ's public subnet | One NAT per AZ, or pin Lambda subnets to AZs with NAT |
The NAT Gateway issue is the one that gets 80% of people. If you attach a Lambda to a VPC, you owe it a route to the internet unless it only talks to private resources. Write that down somewhere. It'll save you an afternoon next time.