-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Description
LambdaRestApi does not trigger new deployment when Lambda is replaced via PhysicalName.GENERATE_IF_NEEDED
Describe the bug
When a LambdaRestApi is backed by a Lambda function using PhysicalName.GENERATE_IF_NEEDED (or any CDK-generated name), changes that cause CloudFormation to replace the Lambda function do not trigger a new API Gateway deployment. The API Gateway continues pointing at the old (now-deleted) Lambda ARN, returning 500 Internal Server Error on every request.
This is a gap in the fix for #5306 (PR #8813). That fix added deployment-token tracking for the Lambda function name, but it's guarded by Token.isUnresolved():
// aws-apigateway/lib/integrations/lambda.js — LambdaIntegration.bind()
let deploymentToken;
Token.isUnresolved(functionName) || (deploymentToken = JSON.stringify({ functionName }));When functionName is a Token (which it is for GENERATE_IF_NEEDED, cross-stack references, or the default unnamed case), deploymentToken remains undefined. Downstream in Method, addToLogicalId receives integrationToken: undefined, so the deployment's logical ID never changes regardless of what happens to the Lambda.
Expected Behavior
Any change to the backing Lambda function — including replacement triggered by a physical name change — should produce a new API Gateway deployment so the stage points at the new function.
Current Behavior
- Deploy a
LambdaRestApiwith a Lambda usingPhysicalName.GENERATE_IF_NEEDED— works fine - Make an unrelated change that alters the Lambda's generated physical name (e.g., changing environment resolution in a cross-stack reference, or modifying the construct tree)
- CloudFormation replaces the Lambda (delete old + create new with different name)
- CloudFormation updates the API Gateway Method integration URI to point at the new Lambda ARN
- No new Deployment is created — the stage still references the old deployment
- All requests return
500 Internal Server Errorbecause the old deployment's cached integration points at the deleted Lambda
There is no warning or error during cdk synth or cdk deploy. The Lambda and API Gateway both appear healthy in the console. The only symptom is silent 500s.
Reproduction Steps
import { Stack, PhysicalName, Duration } from "aws-cdk-lib";
import { LambdaRestApi } from "aws-cdk-lib/aws-apigateway";
import { Function, Runtime, Code } from "aws-cdk-lib/aws-lambda";
// Stack A: Lambda with generated name
const fn = new Function(this, "MyFunction", {
functionName: PhysicalName.GENERATE_IF_NEEDED,
runtime: Runtime.NODEJS_20_X,
handler: "index.handler",
code: Code.fromInline('exports.handler = async () => ({ statusCode: 200, body: "ok" })'),
});
// Stack A: API Gateway
const api = new LambdaRestApi(this, "MyApi", { handler: fn });
// Stack B (references fn from Stack A):
// Any change that alters how Stack B resolves the cross-stack reference
// to fn will change the generated physical name, triggering replacement.
// After deployment: API Gateway returns 500.Real-world trigger: In our case, changing super(scope, id) to super(scope, id, props) in a downstream stack that references the Lambda altered CDK's environment-aware name generation, producing a different physical name and triggering replacement.
Workaround
Add addToLogicalId manually after creating the LambdaRestApi:
const api = new LambdaRestApi(this, "MyApi", { handler: fn });
if (api.latestDeployment) {
api.latestDeployment.addToLogicalId({
lambdaFunctionName: fn.functionName,
});
}This works because addToLogicalId accepts Tokens — it serializes them into the logical ID hash via CloudFormation intrinsics, so when the resolved function name changes, the deployment hash changes too.
Suggested Fix
In LambdaIntegration.bind(), remove the Token.isUnresolved guard and always include the function name in the deployment token. Alternatively, use the Lambda function's logical ID (which is always a concrete string) instead of the physical name:
// Option A: Always include functionName (works with Tokens via CFN intrinsics)
const deploymentToken = JSON.stringify({ functionName });
// Option B: Use the logical resource ID (always concrete)
const deploymentToken = JSON.stringify({
functionLogicalId: this.handler.node.defaultChild?.logicalId
});Environment
- CDK CLI Version: 2.1007.0
- Framework Version: 2.1007.0
- Node.js Version: v24.13.0
- OS: Amazon Linux 2 (CI/CD) / macOS (local)
- Language: TypeScript
Other Information
- The original API Gateway proxy Lambda loses permissions when functionName is changed #5306 fix (PR fix(apigateway): permission error in lambda integration when function name is modified #8813) was specifically for explicit
functionNamechanges, which is why it usedToken.isUnresolved— but the same problem affects any Lambda replacement, not just explicit name changes. PhysicalName.GENERATE_IF_NEEDEDis the recommended pattern for cross-stack Lambda references per CDK docs, making this a very common configuration.- The failure is silent and difficult to diagnose: Lambda logs show zero invocations, API Gateway logs show integration errors, and CloudFormation reports a successful deployment.