OpenTofu Error: 'Saved plan is stale' applying an outdated plan file
Fix OpenTofu's 'Saved plan is stale' error when applying a saved plan file after state changed: diagnose drift, regenerate the plan, and keep plan/apply atomic.
- #opentofu
- #terraform
- #iac
- #troubleshooting
- #errors
Stuck on this OpenTofu error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Exact Error Message
╷
│ Error: Saved plan is stale
│
│ The given plan file can no longer be applied because the state was changed
│ by another operation after the plan was created.
╵
You will see this when you run tofu apply tofu.plan (or whatever you named the plan file) some time after the plan was generated with tofu plan -out.
What It Means
A saved plan is a snapshot. When you run tofu plan -out=tofu.plan, OpenTofu records the exact state serial it planned against and the precise set of changes it intends to make. On tofu apply tofu.plan, OpenTofu re-reads the backend state and compares its serial number to the one embedded in the plan file. If the state has moved on — because another apply ran, someone changed a resource, or a refresh updated the state — the plan no longer describes reality and OpenTofu refuses to apply it.
This is a safety feature, not a bug. Applying a stale plan could create, destroy, or modify resources based on assumptions that are no longer true. OpenTofu forces you to regenerate the plan so what you review is what actually gets applied.
Common Causes
- Another
tofu apply(a teammate, a CI job, or a second pipeline) ran against the same state between your plan and apply. - A
tofu refreshor an apply elsewhere bumped the state serial. - The plan file sat in a CI artifact for hours or days before the apply stage ran, and the world moved on.
- Someone imported or removed a resource from state after the plan was captured.
- Two concurrent pipelines share one backend without state locking, so both plan against the same serial but only one apply “wins.”
Diagnostic Commands
Check the current state serial the backend holds:
tofu state pull | grep '"serial"'
List recent activity to confirm someone else applied:
tofu state list
Regenerate a plan and see whether the diff still matches what you expected:
tofu plan -out=tofu.plan
If you use a remote backend with locking, confirm no lock is held and no run is in flight before you plan again.
Step-by-Step Resolution
-
Do not try to force the old plan. There is no
-forcefor a stale plan by design; the fix is always to replan. -
Pull the current state and confirm what actually changed since your plan:
tofu state pull > current.tfstate
tofu show current.tfstate | less
- Regenerate the plan against the up-to-date state:
tofu plan -out=tofu.plan
-
Review the fresh diff carefully. If a teammate already applied part of your change, the new plan should be smaller — verify it matches your intent.
-
Apply the freshly generated plan promptly to minimise the window for new drift:
tofu apply tofu.plan
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
- In CI, collapse plan and apply into one job or one locked run so the state cannot move between the two stages. If you must split them across pipeline stages, keep the gap short and ensure the backend enforces locking.
Prevention
- Keep the plan-to-apply window as small as possible; a plan file is only valid for the state serial it was made against.
- Enable state locking on your backend (S3 with a lock table, or a native locking backend) so concurrent runs serialise instead of racing.
- In pipelines, run
tofu plan -outandtofu applyin the same stage against the same locked state rather than passing plan artifacts across long-lived, manually-approved gates. - Restrict who and what can apply against production state; ad-hoc applies are the most common source of unexpected serial bumps.
- Treat a stale-plan error as a signal to re-review, not merely to re-run — the change set may genuinely be different now.
Related Errors
Error: Error acquiring the state lock— another run holds the backend lock; wait or investigate the holder.Error: Saved plan does not match the given state— a closely related mismatch when applying a plan built against a different backend or workspace.Error: Backend initialization required— the backend config changed since init and must be re-initialised.Error: Inconsistent dependency lock file— the lock file no longer matches configured providers.
Frequently Asked Questions
Can I force OpenTofu to apply a stale plan anyway? No, and that is intentional. The state moved after the plan was made, so the plan may no longer be safe. Regenerate it with tofu plan -out and apply the fresh file.
Why do I hit this only in CI and never locally? CI often separates plan and apply into different stages with an approval gate between them, leaving a long window for another run to change the state. Keep the two steps in one locked run.
Does state locking prevent this error entirely? Locking prevents two applies from racing, which removes the most common cause, but a legitimately separate apply in between will still make your saved plan stale. The fix remains to replan.
How can I automate a clean replan-and-apply? Have your pipeline run tofu plan -out=tofu.plan immediately before tofu apply tofu.plan within the same locked job. For reusable prompts that scaffold this pattern, see the prompt library at /prompts/?stack=opentofu.
Is there a way to see what changed since my plan? Yes — tofu state pull and tofu show on the pulled state let you compare current reality to the diff you originally reviewed. For more IaC fixes, browse the OpenTofu guides.
Fixed it? Get 500 OpenTofu & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.