InvalidSubnetIDException: The subnet ID provided in the Lambda function VPC configuration is not valid.

Short answer

Lambda validated the VPC configuration before creating anything and rejected an identifier. The subnet or security group either does not exist, sits in a different VPC from its companions, or is in an account this function cannot reach.

What it means

Attaching a function to a VPC means handing Lambda a set of subnets and a set of security groups. Lambda validates them before it creates or updates anything, and rejects the whole call if any identifier does not check out. Nothing is created, nothing runs, and nothing reaches CloudWatch — this lives entirely in the deployment output.

The word "not valid" covers more ground than it appears to. It does not only mean "does not exist". The most frequent cause is a set of identifiers that are each individually real and cannot legally be combined: a security group from one VPC alongside subnets from another. A security group belongs to exactly one VPC, and Lambda cannot place a network interface in a subnet using a group from somewhere else. Both identifiers describe existing resources; the pairing is what is rejected.

That happens more often than it sounds because of how these values are assembled. Subnets usually come from an application or networking stack, while the security group is frequently created later, by hand or in a different module, and defaults to the account's default VPC unless told otherwise. Everything is individually correct, the deploy fails, and the error points at the subnet — which is the half that is right.

Region is the other quiet one. Subnet and security group IDs carry no region information in their format, so subnet-0a1b2c3d from a staging account in another region looks exactly as plausible as the correct value, and refers to nothing where you are deploying.

It is worth separating this from its close neighbour before reaching for a different ID. SubnetIPAddressLimitReachedException means the subnet is entirely correct and has no free addresses left. The messages read similarly, they arrive from the same call, and they want opposite responses: one needs a corrected identifier, the other needs a bigger CIDR. Checking AvailableIpAddressCount distinguishes them in a single command.

Where you'll see it

Deployment output

Not in CloudWatch
$ terraform apply
Error: creating Lambda Function (orders-fn): InvalidSubnetIDException:
The subnet ID provided in the Lambda function VPC configuration is not valid.
with aws_lambda_function.orders,
on lambda.tf line 12, in resource "aws_lambda_function" "orders":
12: resource "aws_lambda_function" "orders" {

This failure happens before the function runs, so nothing about it reaches CloudWatch Logs — there is no invocation, and no log group entry to find. Once the deployment succeeds and the function starts running, the rest of this index covers what you will see there.

Causes, most likely first

1

The subnets and the security group belong to different VPCs

How to confirm

Look up the VPC id of every subnet and every security group in the configuration and check they match. This is the most common cause and the least obvious, because each identifier is individually valid — they simply cannot be combined. A security group from a default VPC alongside subnets from an application VPC is the classic pairing.

2

The identifier is from another region or account

How to confirm

Check the region the deployment targets against where the subnet actually lives. Subnet and security group IDs carry no region in them, so one copied from another environment looks entirely plausible and refers to nothing here.

3

The resource was deleted or replaced

How to confirm

Describe the subnet and security group directly. Infrastructure recreated by another team, or a stack rebuilt without updating a hardcoded value elsewhere, leaves references to identifiers that no longer exist.

4

The subnet has no free IP addresses

How to confirm

Check AvailableIpAddressCount on each subnet. A related exception — SubnetIPAddressLimitReachedException — is raised when the subnet is valid but full, and it reads similarly enough to be mistaken for a bad identifier when it is really a capacity problem.

5

The identifier is a name rather than an ID

How to confirm

Check the value's shape. Lambda wants subnet-0abc… and sg-0abc…. A security group name works in some EC2 APIs and not here, and a Terraform reference resolving to .name rather than .id produces a value that is not an identifier at all.

Fixes

Fix 1

Check every identifier resolves to the same VPC

This one command answers the question that causes most of these, and it answers it before a deploy rather than after.

bashaws ec2 describe-subnets --subnet-ids subnet-0abc subnet-0def \
  --query 'Subnets[].{Subnet:SubnetId,Vpc:VpcId,AZ:AvailabilityZone,Free:AvailableIpAddressCount}'

aws ec2 describe-security-groups --group-ids sg-0abc \
  --query 'SecurityGroups[].{Group:GroupId,Vpc:VpcId}'
Fix 2

Reference the resources rather than hardcoding their IDs

In CloudFormation, SAM or Terraform, referring to the subnet and security group resources makes it impossible to point at something deleted, and keeps the VPC consistent by construction.

yamlResources:
  OrdersFunction:
    Type: AWS::Serverless::Function
    Properties:
      VpcConfig:
        SubnetIds:
          - !Ref PrivateSubnetA
          - !Ref PrivateSubnetB
        SecurityGroupIds:
          - !Ref LambdaSecurityGroup
Fix 3

Distinguish "not valid" from "full" before changing the ID

SubnetIPAddressLimitReachedException means the subnet is correct and out of addresses. The fix there is a larger CIDR or additional subnets, not a different identifier — and swapping the ID because the messages read alike wastes a deploy cycle.

bashaws ec2 describe-subnets --subnet-ids subnet-0abc \
  --query 'Subnets[0].{Cidr:CidrBlock,Free:AvailableIpAddressCount}'
Fix 4

Use the ID attribute, not the name

Where a template is generating the value, check it resolves to an identifier. Security groups in particular have both, and only one of them works here.

hclresource "aws_lambda_function" "orders" {
  vpc_config {
    subnet_ids         = [for s in aws_subnet.private : s.id]
    security_group_ids = [aws_security_group.lambda.id]
  }
}

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.

InvalidSecurityGroupIDException: The security group ID provided in the Lambda function VPC configuration is not valid.The subnet ID provided in the Lambda function VPC configuration is not validSubnetIPAddressLimitReachedExceptionlambda invalid subnet idlambda invalid security group id

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

References

This one happens before there are any logs.

LogStitch reads CloudWatch, and a deployment that fails never writes to it — so this is not an error it can find for you. Once the function deploys and starts running, the free web stitcher groups its invocations in your browser, and the Mac app does the same across every function in your account.