What it means
Updating a Lambda function is not atomic and it is not instant. When you change code or configuration, the function enters a transitional state while Lambda validates the change, provisions whatever it needs, and rolls the new version into place. During that window the function keeps serving traffic on its existing configuration — and refuses any further updates.
That refusal is ResourceConflictException. It is a guard, not a fault: allowing a second update while the first is settling would leave the function in a state neither deployment intended.
Nearly every occurrence comes from a script doing two things in a row. UpdateFunctionCode returns as soon as Lambda has accepted the new code, not when the function is ready with it, so a following UpdateFunctionConfiguration a few hundred milliseconds later arrives mid-update and is rejected. The script is not wrong about the order it wants; it is wrong about when the first call finished.
Two things make the window longer than people expect, and both turn a deployment that worked for months into one that fails intermittently. VPC configuration requires Lambda to provision elastic network interfaces, which takes meaningfully longer than a plain update. Layers and container images take longer to settle than a zip. In both cases the fixed delay someone added years ago quietly stops being enough, and the failure looks random because it depends on how busy the control plane is that minute.
The right answer is to wait on the actual state rather than on a clock. aws lambda wait function-updated and the LastUpdateStatus field both report when the function is genuinely ready, and neither needs tuning as the function grows. None of this appears in CloudWatch Logs — the function is running normally throughout, serving its previous configuration — so the only place this exists is in the deployment's own output.
Where you'll see it
Deployment output
Not in CloudWatchThis 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
Code and configuration are being updated back to back
Look at what the deployment does immediately before the failing call. UpdateFunctionCode followed straight away by UpdateFunctionConfiguration is the classic pattern: the first returns as soon as it is accepted, not when it is finished, so the second arrives while the function is still settling.
Two pipelines are deploying the same function at once
Check for concurrent runs — two merges to main, a manual deploy overlapping a scheduled one, or a matrix job that fans out over environments sharing a function. Lambda serialises updates per function and rejects whichever arrives second.
The function is still being created
Read the state in the message. Pending means creation has not completed, which is common for a VPC-attached function where Lambda is still provisioning network interfaces — that can take considerably longer than a plain function.
A layer or image update is still propagating
Check whether the deployment changed a layer version or a container image. Those updates take longer to settle than a plain zip, so a delay that was sufficient for code-only deployments starts failing once a layer is added.
Fixes
Wait for the function to become active before updating it again
The AWS CLI has waiters for exactly this, and they are the correct answer rather than a sleep. function-updated blocks until the previous update has settled.
bashaws lambda update-function-code \
--function-name orders-fn \
--zip-file fileb://function.zip
# Block until the previous update has actually finished.
aws lambda wait function-updated --function-name orders-fn
aws lambda update-function-configuration \
--function-name orders-fn \
--environment 'Variables={LOG_LEVEL=debug}'
Poll LastUpdateStatus when you cannot use a waiter
In an SDK, the equivalent is checking LastUpdateStatus until it leaves InProgress. The terminal values are Successful and Failed, and treating Failed as done — rather than looping forever — is the part most hand-rolled versions get wrong.
jsimport { LambdaClient, GetFunctionConfigurationCommand } from "@aws-sdk/client-lambda";
async function waitForUpdate(client, name, attempts = 30) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
const config = await client.send(
new GetFunctionConfigurationCommand({ FunctionName: name })
);
if (config.LastUpdateStatus !== "InProgress") {
if (config.LastUpdateStatus === "Failed") {
throw new Error(`Update failed: ${config.LastUpdateStatusReason}`);
}
return config;
}
await new Promise((resolve) => setTimeout(resolve, 2000));
}
throw new Error(`${name} still updating after ${attempts} checks`);
}
Stop two pipelines deploying the same function
Where the cause is concurrency rather than sequencing, the fix is at the pipeline level. GitHub Actions concurrency groups cancel or queue overlapping runs, which removes the race instead of retrying through it.
yamlconcurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false
Let CloudFormation handle the sequencing
CloudFormation and SAM already wait for each resource to stabilise before moving on, so a deployment expressed as a stack update does not hit this at all. Most occurrences come from scripts issuing raw API calls in sequence.
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.
Related errors
Errors that show up alongside this one, or that people mistake for it.
References
- AWS Lambda Developer Guide — Lambda function states
- AWS Lambda Developer Guide — Troubleshoot deployment issues
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.