Fix AWS Lambda ResourceConflictException: "An Update Is in Progress" Error During Deployment
If AWS Lambda is throwing ResourceConflictException: The operation cannot be performed at this time. An update is in progress for resource, the fix is almost always to stop calling UpdateFunctionCode or UpdateFunctionConfiguration again until the previous update finishes — Lambda tracks that with a field called LastUpdateStatus, and while it reads InProgress, every further update request gets bounced with a 409 error. Here's the part that surprises almost everyone the first time they hit it: your function is not broken, and it is not down. It's still answering requests the entire time — just with the old code, because Lambda deliberately keeps serving the last successful version until the new one is fully rolled out.
What ResourceConflictException actually means
Every Lambda function carries two separate status fields, and mixing them up is the single biggest reason this error confuses people who are otherwise perfectly competent at their jobs.
The first is State — think of it as "does this function exist and is it ready to be invoked at all." It's one of Pending, Active, Failed, or Inactive. A brand-new function sits in Pending for a few seconds — longer if it's attached to a VPC, more on that further down — while Lambda provisions the resources it needs, then flips to Active.
The second is LastUpdateStatus — a completely different tracker for "is the most recent code or configuration change still being rolled out." It's one of Successful, Failed, or InProgress. This is the field that matters for the error you're actually seeing. Here's the detail that trips people up: while LastUpdateStatus is InProgress, State stays Active the entire time. The function looks completely healthy from the outside. It's still being invoked. It's just quietly serving the previous version's code and settings while the new one finishes deploying behind the scenes.
Lambda has exactly two API operations that change a function's deployed code or settings — UpdateFunctionCode and UpdateFunctionConfiguration — and both are asynchronous, which is a term worth unpacking the first time you meet it. An asynchronous operation is one where the system hands you back control immediately and keeps working in the background, rather than making you sit and wait until it's fully done — it's the difference between dropping a phone off at a repair counter and getting a ticket, versus standing there while the technician takes it apart in front of you. You call UpdateFunctionCode, Lambda immediately marks LastUpdateStatus as InProgress and gives you your ticket back, then keeps working. If you — or your deploy tool, or a second person on your team — call either update operation again, or call PublishVersion or TagResource, before that first change finishes, Lambda refuses the second call outright and throws ResourceConflictException with an HTTP 409 status code, which is the standard web status for "this request conflicts with the current state of the resource you're trying to change." It isn't a bug and it isn't Lambda struggling under load. It's a lock, and you just tried to grab it twice.
Ethan's take is blunt about it: "People treat this like Lambda is being difficult. It's the opposite — this is one of the few AWS error messages that's actually protecting you. Without that lock, two overlapping updates could land in an order you didn't intend, and you'd have no idea which change actually won."
🙋♂️ Jake's Reality Check
"So when this fires, is the app my customers are using right now actually broken? Because I've got someone in the shop waiting on a repair-status text that goes through one of these functions."
No — almost certainly not. While LastUpdateStatus is InProgress, the function keeps invoking normally on its previous code and configuration. Your customer's text still goes out. What's blocked is your next deploy attempt, not the function's ability to run right now. The one exception is a brand-new function still in the Pending State — that one genuinely can't be invoked yet, and that's a different (if related) error text, covered further down.
Step one: find out which of the three situations you're actually in
Before you do anything else, pull the function's real status. Guessing wastes more time than checking does, and it's the difference between a fix that takes ten seconds and a fix that takes ten minutes of trial and error:
aws lambda get-function --function-name YOUR-FUNCTION-NAME --query 'Configuration.[State, LastUpdateStatus]'
A healthy function that's simply finishing a deploy will print something like ["Active", "InProgress"]. That's the normal, boring, self-resolving case. The table below covers every combination you'll actually see and what each one means for you.
| State | LastUpdateStatus | What's really happening |
|---|---|---|
| Active | InProgress | Normal. An update is mid-flight. Invocations run the previous code. Just wait. |
| Active | Successful | Nothing is blocking you. If you're still seeing the 409, the request that hit it has already cleared — retry now. |
| Active | Failed | The last update attempt errored out. The function still runs its previous code, but you need to fix the cause and redeploy — see the FAQ on Failed status. |
| Pending | (new function, not shown yet) | Function can't be invoked at all yet — usually because it's provisioning VPC network interfaces. See the VPC section. |
Most people land in row one. If that's you, skip straight to the wait-it-out section below — it's a two-minute fix, not an investigation. If you're not sure how to read the output, it's worth explaining the command itself once: the --query flag is asking the CLI to hand back only the two fields you actually care about, instead of dumping the entire function configuration — dozens of lines about memory, timeout, layers, and IAM role — at you. It's the same instinct as asking a mechanic "just tell me if the brakes are fine," rather than reading the whole diagnostic printout yourself.
What this actually costs when nobody understands it
Jake ran into this the way most people do: not while reading documentation, but during a live deploy on a Friday afternoon. His shop uses a small Lambda function to text customers when a phone repair is marked "ready for pickup," wired up to a status field in his repair-tracking system. He'd asked a contractor to add a new field to that text — the estimated cost — and the contractor's deploy script updated the function's code, then immediately tried to add a new environment variable for the pricing API key. The second call failed with ResourceConflictException.
The actual damage wasn't the error itself — it was what happened next. The contractor, unsure whether the first half of the deploy had landed, ran the whole script again. And then, when that failed too, a third time. Three overlapping update attempts in about ninety seconds, each one racing the ones before it. What should have been a thirty-second annoyance turned into forty-five minutes of confusion about which version of the code was actually live, because nobody had checked LastUpdateStatus before retrying.
Ethan's opinion on this is the whole reason this section exists: "The error itself never cost Jake a dime. The panicked re-running of the same broken script three times in a row is what cost him the afternoon. If you take away one thing from this post, make it this: seeing ResourceConflictException is not a signal to try again immediately. It's a signal to go check the status first."
Route 1: it's genuinely mid-update — wait for it properly
The instinct is to hammer refresh, or retry the deploy command again immediately. Don't — that's exactly the behavior that produced the error in the first place, and doing it again just produces the same 409 a second time, as Jake found out the hard way above. What you actually want is to poll LastUpdateStatus until it flips to Successful, and there are three ways to do that depending on what's calling the update.
If you're using the AWS CLI
The CLI ships a built-in waiter that does exactly this — it calls GetFunctionConfiguration every five seconds until LastUpdateStatus reads Successful, and gives up with an error after 60 failed checks, which works out to five minutes of polling:
aws lambda update-function-code --function-name YOUR-FUNCTION --zip-file fileb://deploy.zip
aws lambda wait function-updated --function-name YOUR-FUNCTION
aws lambda update-function-configuration --function-name YOUR-FUNCTION --environment "Variables={KEY=value}"
That middle line is the whole fix for the most common cause of this error: scripts that update code and then immediately update configuration in the same breath, with nothing in between to let the first change finish. A "waiter," if the term is new to you, is exactly what it sounds like — a small helper built into the CLI and SDKs whose entire job is to sit there checking a status field for you on a fixed schedule, so you don't have to write that polling loop by hand every single time.
If you're using boto3 (Python)
boto3's Lambda client exposes the equivalent waiters by name — function_updated and its newer sibling function_updated_v2, alongside function_active, function_active_v2, and function_exists:
import boto3
client = boto3.client("lambda")
client.update_function_code(FunctionName="YOUR-FUNCTION", ZipFile=open("deploy.zip", "rb").read())
waiter = client.get_waiter("function_updated")
waiter.wait(FunctionName="YOUR-FUNCTION")
client.update_function_configuration(
FunctionName="YOUR-FUNCTION",
Environment={"Variables": {"KEY": "value"}}
)
The pattern is identical to the CLI: update, wait, update. Never fire two Lambda-mutating calls back to back without a wait step between them, no matter which SDK you're writing in — every language SDK Lambda ships (Java, Go, .NET, Ruby, JavaScript) exposes some form of this same function_updated waiter concept, because they're all polling the same underlying LastUpdateStatus field on the same underlying API.
If you can't use a waiter
Some environments — a Lambda function deploying another Lambda function, for instance, where you don't want to block execution for minutes at a time — need to poll manually instead. The steps below are the same ones the built-in waiters run internally, so you're not inventing a new approach, just implementing the existing one yourself:
- Call
GetFunctionConfiguration(orGetFunction) and read theLastUpdateStatusfield from the response. - If it's
InProgress, sleep for a few seconds — five is a reasonable interval, matching what the built-in waiters use — then check again. - If it's
Successful, proceed with your next update call. - If it's
Failed, stop and readLastUpdateStatusReasonandLastUpdateStatusReasonCodein the same response — they name the actual cause, whether that's a bad IAM role, an invalid environment variable, or a missing container image. - Cap your retries. If you're still seeing
InProgressafter several minutes, treat it as stuck rather than looping forever — that's the next section.
✅ Why this is the one to use
A built-in waiter beats a hand-rolled sleep loop for one reason: it already encodes the correct interval and give-up point, so you're not guessing at how long is "too long" or accidentally polling so aggressively you get throttled. Reach for the manual loop only when a waiter genuinely isn't available in your environment.
Route 2: it's been stuck for several minutes, not seconds
Occasionally an update genuinely wedges — LastUpdateStatus sits at InProgress for far longer than the usual few seconds, sometimes for many minutes. If a function has been in this state for more than six minutes, you have a documented, official way out: call any of UpdateFunctionCode, UpdateFunctionConfiguration, or PublishVersion again anyway. Lambda cancels the pending operation, moves the function into the Failed state, and clears the way for a fresh attempt.
⚠️ What this actually breaks
Forcing the cancel only works after roughly six minutes of being stuck — trying it earlier just gets you the same ResourceConflictException again, because the update genuinely hasn't had a chance to finish yet. Don't force-cancel at the 30-second mark just because you're impatient; you'll burn a retry for nothing, and you'll be right back where Jake's contractor ended up.
Ethan pushes back on treating this as a routine tool: "I wouldn't build the six-minute cancel into a script that runs unattended. It's a break-glass move for a human who's looked at the situation and decided the update is actually dead, not a step you automate blindly — because if the update was slow rather than dead, you've just thrown away work that was about to finish on its own."
Same idea for a function stuck in Pending (not Active)
A different but related error text shows up when a new function is created with a VPC attachment: ResourceConflictException: The operation cannot be performed at this time. The function is currently in the following state: Pending. This happens because Lambda is creating elastic network interfaces (ENIs) for the VPC, and the function literally cannot be invoked or modified until that finishes. If you attach a function to a VPC after it already exists, the rule is looser — you can still invoke it while the update is pending, you just can't change its code or configuration during that window.
🕐 What changed between versions
- Before: VPC-attached functions could sit in
Pendingfor a long time while Lambda built out ENIs, one interface per subnet/security-group combination your function used. - Now: Lambda's networking model creates and shares ENIs across functions using the same VPC/subnet/security-group combination, so most new VPC functions clear
Pendingin well under a minute. - What that means for you: if a VPC-attached function is stuck in
Pendingfor more than a few minutes today, that's unusual enough to be worth investigating on its own — check that your subnets have free IP addresses left, and that your security groups aren't blocking the interfaces Lambda needs to create.
Why this fires on nearly every deploy for some teams
If you're only seeing this error occasionally, waiting it out is enough. But if it fires on every single deploy, that's not bad luck — it's a pattern baked into your pipeline, and it'll keep happening until you fix the pattern itself, not the individual failed deploy. There are exactly two shapes this takes in practice.
Shape one: code, then config, back to back
Plenty of deploy scripts push new code with UpdateFunctionCode, then immediately set environment variables, memory, or timeout with UpdateFunctionConfiguration — because logically those are "one deploy" to a human. To Lambda's API, they're two separate asynchronous operations. The second call races the first one's completion, and if the code deployment package is large or the runtime is still validating it, the second call loses that race and gets the 409. This is the most common root cause reported against real deploy tooling, and it's precisely what the wait-then-update pattern above solves.
Shape two: two things deploying the same function at once
The sneakier version: two separate processes both trying to update the same function within the same window. A CI retry firing off a second run while the first is still deploying. A developer manually running update-function-code from their laptop while the pipeline is mid-deploy on the same branch. A CI matrix job that accidentally targets the same function name from two parallel legs. None of these show up as "your code" — they show up as unpredictable, hard-to-reproduce 409s that seem to come and go depending on timing, which is exactly what makes them frustrating to debug.
🙋♂️ Jake's Reality Check
"Okay but this only started happening this week. Nobody changed the deploy script. What gives?"
Look at what else changed, not the script. A deployment package that grew in size, a new teammate who now also has deploy access and is testing changes at the same time as the pipeline, or a CI tool that was recently reconfigured to retry failed steps automatically are all common triggers. The script staying the same doesn't mean the conditions around it did.
Ethan calls this the "it worked yesterday" trap: "The script isn't lying to you when someone says nothing changed — the timing changed. A function that took four seconds to update last month might take nine seconds today because the deployment package grew, and that's enough to turn a race that never mattered into one that fires every time."
Fixing it in the tool you actually deploy with
"Just add a wait step" is easy advice when you're calling the API directly. It's less obvious when Terraform, the Serverless Framework, or SAM is the thing making the calls on your behalf. Here's how each one behaves, and where the friction actually shows up.
| Tool | Where the conflict shows up | What to change |
|---|---|---|
Terraform (aws_lambda_function) |
Usually on publish = true functions — the provider updates code, then tries to publish a version before Lambda has finished processing the code update. |
Re-run terraform apply once the first update clears — Terraform's own state locking prevents two applies hitting the same resource at once, so a genuine race between two people is rare; a stale provider version is the more common culprit, so keep the AWS provider current. |
| Serverless Framework / AWS SAM | Rare on a normal deploy, since both drive changes through a CloudFormation stack, and stack updates are inherently sequential for a given stack. | If it does happen, check for a second deploy process — a teammate, a scheduled job, another CI run — targeting the same stack at the same time. That's a stack-level conflict wearing a Lambda error. |
| Custom CI scripts (bash, Python, Node calling the SDK directly) | Most exposed — no framework is inserting a wait for you, so a code-then-config script hits this reliably. | Add the waiter pattern from the section above between every pair of update calls. |
| Older or unmaintained third-party deploy plugins | Some early Lambda deploy tools predate the waiter pattern being common practice and simply fire calls with no gap. | Check the plugin's changelog for a fix, or wrap its call in your own retry-with-backoff logic as a stopgap. |
CI platforms: stop the second run before it starts
Most CI platforms — GitHub Actions, GitLab CI, and others — support some form of "concurrency group" or "deploy lock" on a job or workflow, which lets you say "only one instance of this deploy job may run at a time for this function or environment; queue or cancel the rest." That setting solves shape two above at the source: instead of teaching every script to survive a collision, you stop the collision from being possible in the first place. It's worth setting up once per pipeline rather than relying on waiter logic alone to paper over overlapping runs.
A different 409 you'll see on container image functions
If your function deploys as a container image rather than a .zip file, you can hit a family of related-but-distinct 409 errors during the optimization step Lambda runs right after you push a new image:
- CodeArtifactUserPendingException — the image is still being optimized. The function moves to the
Activestate once optimization completes; this one resolves itself the same wayInProgressdoes — wait, then retry. - CodeArtifactUserDeletedException — the image is scheduled for deletion. Push a new image; there's nothing to wait for here.
- CodeArtifactUserFailedException — Lambda couldn't optimize the code. This needs an actual fix to the image, not a wait; correct it and push again.
These aren't ResourceConflictException by name, but they're the same family of "something's still in flight, don't push another change yet" error, wearing a container-specific label. If your team runs both .zip and image-based functions, it's worth documenting this distinction somewhere your on-call engineers will actually see it — the panic response is the same either way (check status, wait, don't retry blindly), but the exact exception name changes and a search for the wrong term wastes time at 2 a.m.
Edge cases: provisioned concurrency, aliases, versions, and SnapStart
A handful of less common situations are worth naming explicitly, because the underlying cause is the same "one change at a time" rule, but the fix looks slightly different in each case.
Publishing a version right after a code update. PublishVersion — which takes a snapshot of your function's current code and configuration and locks it under a permanent version number, the way saving a "final" copy of a document freezes it against further edits — is on the same blocked list as the two update calls. You can't publish a new version while LastUpdateStatus is still InProgress from the code push that version is supposed to capture. Use the function_updated waiter (or its CLI equivalent) before calling PublishVersion, exactly as you would before a second UpdateFunctionConfiguration call.
Provisioned concurrency mid-deploy. Provisioned concurrency keeps a set number of execution environments warmed up and ready in advance, so requests don't pay the "cold start" tax of Lambda initializing a fresh environment on demand — think of it like a restaurant kitchen keeping a few burners already lit before the dinner rush, instead of lighting them the moment the first order comes in. If a function or alias has provisioned concurrency configured, updating the function's code while that configuration is being applied can produce conflicting in-flight operations of its own — Lambda needs to finish initializing those warmed environments before it will accept another change. If you manage provisioned concurrency alongside code deploys, wait for the provisioned concurrency status to settle first, the same way you'd wait for LastUpdateStatus.
Alias weighted routing. An alias mid-way through a canary or linear traffic shift between two versions — gradually moving traffic from the old version to the new one instead of switching everyone over instantly — is its own kind of "in progress." Changing the alias's routing configuration again before the current shift settles can produce a conflict for the same underlying reason: one change per resource, one at a time.
SnapStart functions. Functions using SnapStart, which lets Lambda restore from a pre-initialized snapshot instead of running your startup code cold every time, can stay in the Pending state noticeably longer than an ordinary function after you publish a new version — initialization code is allowed to run for up to 15 minutes while Lambda builds the snapshot. If you try to invoke that version while it's still pending, you may see a 409 ResourceConflictException, or a 500 error from API Gateway if that's what's invoking it. The fix here isn't a five-second waiter loop — it's simply expecting a longer wait window (AWS's own guidance is to allow at least 15 minutes) before treating a SnapStart version as ready.
The angle most teams skip: who's actually allowed to deploy?
Shape two of the pipeline problem — two things updating the same function at once — usually traces back to a permissions question nobody ever answered on purpose: who, and what, is allowed to call UpdateFunctionCode and UpdateFunctionConfiguration on a given function? If both your CI pipeline's role and every individual developer's IAM user have that permission on production functions, you've built a system where a collision is a matter of "when," not "if."
This is worth treating as a genuine access-control decision, not just a deploy-script nuisance. A reasonable default for anything beyond a solo project: production functions get updated only through CI, using a role scoped to that pipeline, and individual developers' IAM permissions stop short of lambda:UpdateFunctionCode and lambda:UpdateFunctionConfiguration on those specific functions — they can still invoke, read logs, and view configuration, just not push changes directly. That one boundary removes an entire category of "why did this deploy fail" investigations, because there's structurally only one thing that can ever be mid-update at a time.
🙋♂️ Jake's Reality Check
"That sounds like a lot of process for a two-person shop. Do I really need to lock myself out of my own function?"
Not necessarily — but know the trade-off you're making. For a small setup, "only one person deploys, and they check status before deploying" can substitute for a hard permissions boundary. The moment a second person, or a second automated process, gets deploy access to the same function, that informal agreement stops being enough, because nobody can see what the other one is doing in real time.
Automating deploys without reintroducing the problem
Once a team is deploying Lambda functions frequently enough to want automation beyond a single CI job — say, orchestrating a rollout across several related functions, or gating a config change behind an approval step — it's tempting to reach for something that queues up several update calls in sequence. The waiter discipline from earlier doesn't go away just because the orchestration got fancier; if anything it matters more, because an orchestrator with no wait logic will happily fire a dozen conflicting calls in the time it takes a human to fire one.
A step-based workflow tool that supports waiting on external state — polling a status field, pausing, then checking again — is a natural fit here, because that's precisely the shape of the problem: update, wait for LastUpdateStatus to settle, then move to the next step. If you're building this kind of orchestration yourself with a Lambda function that deploys other Lambda functions, apply the same manual-polling steps from earlier rather than assuming the orchestrating function can simply fire-and-forget its update calls one after another.
Do you need a third-party deploy tool for this?
Honestly, usually not, and it's worth saying that plainly instead of pretending you need to buy or adopt something new. The waiter pattern above is built into the AWS CLI and every official SDK at no extra cost and no extra dependency — it's not a gap that a third-party tool is uniquely positioned to fill. Where a heavier deploy tool (the Serverless Framework, SAM, or a custom internal platform) earns its keep isn't the waiter logic itself; it's everything around the deploy — packaging, versioning conventions, rollback tooling, multi-environment promotion. If your only problem is "my script sometimes hits ResourceConflictException," adopting a new framework to solve it is solving a five-line problem with a much bigger tool than it needs.
Ethan is opinionated about this one too: "I've watched teams add an entire deployment platform because of a error that a single aws lambda wait call would have fixed. Reach for the framework when you actually need what the framework does — multi-service orchestration, environment promotion, rollback history. Don't reach for it because a 409 scared you."
Making sure this doesn't come back
Once you've unblocked today's deploy, spend ten minutes making sure this doesn't turn into a recurring interruption:
- Wrap every update call in a waiter. This is the fix that matters most — see the code patterns above for the CLI and boto3.
- Restrict who and what can deploy to a given function concurrently. Use your CI system's concurrency groups or deploy locks so two pipeline runs can't target the same function at once, and use IAM permissions to keep manual deploys off functions CI also manages.
- Ask developers to stop deploying by hand to functions CI also deploys to. A manual
update-function-codefrom someone's laptop, run at the same moment CI is deploying, is a common and completely avoidable source of this error. - Add a short, bounded retry with backoff around the update calls themselves — a few attempts a few seconds apart — as a safety net for the rare cases the waiter alone doesn't catch, like a retried CI step.
- If you use CloudFormation, SAM, or the Serverless Framework, keep deploys to one in-flight update per stack. Let one deploy finish, or fail cleanly, before starting the next one against the same stack.
✅ Why this is the one to use
Of all five, the waiter is doing 90% of the work. Deploy locks and manual-deploy discipline matter for teams, but a solo developer or small shop that just adds the wait step between calls will never see this error again on a normal deploy.
Frequently asked questions
What does "An update is in progress for resource" actually mean?
It means Lambda's LastUpdateStatus field for that function currently reads InProgress — the previous code or configuration change hasn't finished processing yet, and Lambda is refusing to accept a second overlapping change until it does.
Is my Lambda function down when I see this error?
No. The function's State stays Active the whole time, and it keeps serving invocations using its previous, already-deployed code and configuration. The only thing that's blocked is your next change request, not the function's ability to run.
How long should I wait before retrying?
Most updates clear in well under a minute. Rather than guessing a fixed wait, use the built-in function_updated waiter (CLI or SDK) — it polls every five seconds and moves on the instant the status changes, so you're never waiting longer than necessary.
My function has been stuck in Pending for 10 minutes — what do I do?
Once a function has been stuck for more than six minutes, calling UpdateFunctionCode, UpdateFunctionConfiguration, or PublishVersion again cancels the pending operation and moves the function to the Failed state, which frees it up for a fresh, clean attempt.
Why does this happen every time my CI/CD pipeline deploys?
Almost always because the pipeline calls UpdateFunctionCode and then immediately calls UpdateFunctionConfiguration (or PublishVersion) without waiting for the first call to finish. Add a wait step between the two calls and it stops happening.
Can I update code and configuration in the same request to avoid this?
No — Lambda exposes them as two distinct API operations, UpdateFunctionCode and UpdateFunctionConfiguration, with no combined single call. The fix isn't merging them; it's waiting for one to finish before firing the other.
Does Terraform handle this automatically?
Terraform's state locking prevents two apply runs from touching the same resource simultaneously, which rules out one cause of the conflict. It doesn't insert a wait between an in-flight Lambda update and a subsequent apply targeting the same function, so a second apply run too soon after the first can still hit it — simply re-running apply once the first update clears resolves it.
Does the Serverless Framework or SAM handle this automatically?
Both deploy through a CloudFormation stack, and CloudFormation processes changes to a given stack sequentially, so this is rare on a normal solo deploy. If you do see it with either tool, look for a second deploy — from a teammate, a scheduled job, or another CI run — targeting the same stack at the same time.
Why do I get this error only on functions attached to a VPC?
A newly created VPC-attached function sits in the Pending state while Lambda provisions elastic network interfaces for it, and any invoke or modify attempt during that window fails with a related — but textually different — version of this error naming the Pending state directly. If you attach an existing function to a VPC afterward, you can still invoke it during the pending window; you just can't change its code or settings until it finishes.
What's the difference between ResourceConflictException here and CodeArtifactUserPendingException?
They cover the same underlying idea — "something about this function is still being processed" — but apply to different deployment types. ResourceConflictException with the "update is in progress" text is the general case for any function. CodeArtifactUserPendingException is specific to container image functions still being optimized after a new image push.
Will retrying automatically make things worse (rate limiting)?
A tight retry loop with no backoff wastes API calls and can contribute to hitting Lambda's request throughput limits on top of the conflict itself. Keep retries bounded and spaced out — the built-in waiter's five-second interval is a sensible baseline — rather than looping as fast as possible.
Can two people deploy the same function at the same time safely?
Not safely, no — Lambda only allows one code or configuration change to be in flight per function at a time, so a second person's deploy attempt during someone else's in-flight update will hit this exact error. If your team deploys the same functions manually and via CI, establish a rule about who deploys when, or restrict update permissions on production functions to CI only.
Does publishing a version trigger this too?
Yes. PublishVersion is blocked during an in-progress update exactly like the two update operations are, and it can also be the call that triggers the conflict if it's fired right after a code update finishes but before LastUpdateStatus catches up.
Why does GetFunctionConfiguration show LastUpdateStatus: Failed and what do I do?
A Failed status means the previous update attempt itself hit an error — a bad IAM role, an invalid setting, a corrupt deployment package, and so on — not that a conflict is blocking you. Read LastUpdateStatusReason and LastUpdateStatusReasonCode from the same API response; they name the actual problem, and the function keeps running its previous code until you fix that problem and redeploy.
Does this affect Lambda@Edge or container image functions differently?
Container image functions add their own related 409 family during image optimization, covered above, and SnapStart-enabled functions can legitimately stay pending far longer than usual while their snapshot builds. The core LastUpdateStatus/ResourceConflictException mechanism applies the same way regardless of package type — it's about the function resource itself, not how the code is packaged.
How do I stop this from ever happening again?
Put a waiter between every pair of Lambda-mutating calls in your deploy scripts, make sure only one deploy path can touch a given function at a time — through both CI concurrency controls and IAM permissions — and add a short bounded retry as a safety net for the rare timing edge case. That's the whole fix — there's no account-level setting or quota to raise here, because the "one update at a time" rule isn't a limit, it's how the API is designed to work.
Revision note. Written September 2026. This will need a fresh look if AWS changes how concurrent update handling works at the API level or introduces a combined update operation. If you're staring at a failed deploy right now, take a breath — the fix here is genuinely a five-minute one, and your function is very likely still serving customers just fine while you sort it out.