Terraform Error: 'refresh-only plan cannot be applied' When Applying a Saved Plan
Fix Terraform's 'plan was created with the -refresh-only option and cannot be applied' error: understand refresh-only mode, apply state changes correctly, and regenerate a normal plan.
- #terraform
- #iac
- #troubleshooting
- #errors
Stuck on this Terraform 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: Failed to load "tfplan": plan was created with the -refresh-only option
│ and so cannot be applied.
│
│ A refresh-only plan can only update the Terraform state to match the real
│ infrastructure; it cannot make changes to remote objects.
╵
You may hit this after running:
terraform plan -refresh-only -out=tfplan
terraform apply tfplan
What It Means
terraform plan -refresh-only produces a plan that only reconciles Terraform’s state with the real world — it detects drift and updates the recorded attributes, but it deliberately proposes no changes to your infrastructure. Terraform marks the saved plan file as refresh-only.
terraform apply <planfile> is meant to execute a set of proposed resource changes. When you hand it a refresh-only plan, Terraform refuses: there are no infrastructure changes to make, only a state update, and the apply-a-saved-plan path is not the mechanism for that. The fix depends on what you actually intended — to record drift into state, or to make real changes.
Common Causes
- You saved a plan with
-refresh-only -out=...and then triedterraform apply <planfile>, expecting it to update state. - A CI pipeline uses a generic “plan then apply the saved file” flow, and a refresh-only step was slotted into it.
- Confusion between
-refresh-only(state reconciliation) and a normal plan (infrastructure changes). - An attempt to “accept drift” using the saved-plan workflow instead of the interactive refresh-only apply.
Diagnostic Commands
Confirm how the saved plan was created by inspecting it (a refresh-only plan shows no resource actions):
terraform show tfplan
Check for drift without saving anything, to decide what you actually want:
terraform plan -refresh-only
Review the current state and what Terraform believes exists:
terraform state list
terraform show
Step-by-Step Resolution
Pick the path that matches your intent.
- If you wanted to record drift into state (accept that the real infrastructure changed and update Terraform’s record), do not use a saved plan file. Run refresh-only apply directly and approve it:
terraform apply -refresh-only
Terraform prints the state-only changes and prompts for yes. This updates state without touching infrastructure.
- To do the same non-interactively in CI, auto-approve:
terraform apply -refresh-only -auto-approve
- If you actually wanted to change infrastructure, generate a normal plan (without
-refresh-only) and apply that:
terraform plan -out=tfplan
terraform apply tfplan
- If drift and real changes are both present, first reconcile state, then plan the real changes cleanly:
terraform apply -refresh-only -auto-approve
terraform plan -out=tfplan
terraform apply tfplan
- Verify the outcome. After a refresh-only apply, a follow-up plan should report the state now matches reality (or show only the genuine changes you intend):
terraform plan
No changes. Your infrastructure matches the configuration.
If your pipeline blends drift detection and change application into one step, the Terraform workflow prompts in the prompt library can help you split refresh-only drift checks from apply stages so this error stops recurring.
Prevention
- Keep drift detection and change application as separate pipeline stages; never feed a refresh-only plan into an apply-saved-plan step.
- Remember the rule:
-refresh-onlyupdates state only; a normal plan updates infrastructure. - For accepting drift, use
terraform apply -refresh-onlyinteractively (or-auto-approvein automation), not a saved plan file. - Name saved plan files by intent (for example
drift-check.planvsapply.plan) so they are not mixed up. - Run scheduled
-refresh-onlyplans to surface drift early, and act on it before it compounds. - Review
terraform show <planfile>in CI before applying so a zero-change plan is caught.
Related Errors
Saved plan is stale— the state changed after the plan was created, a different saved-plan problem.Error: Cannot apply incomplete plan— the plan file is corrupt or truncated.Note: Objects have changed outside of Terraform— drift detected during a normal plan, which refresh-only addresses.No changes. Your infrastructure matches the configuration.— not an error; the expected result after reconciling drift.
Frequently Asked Questions
What is the point of -refresh-only if it cannot be applied as a saved plan? It exists to reconcile state with reality without proposing infrastructure changes; you apply it with terraform apply -refresh-only, not by passing the saved file to apply.
How do I accept detected drift into state? Run terraform apply -refresh-only and approve it, or -auto-approve in CI; this updates the recorded attributes to match the real objects.
Why does saving the plan then applying fail specifically here? The saved-plan apply path executes proposed resource actions, and a refresh-only plan contains none, so Terraform blocks it to avoid a confusing no-op.
Can I combine drift acceptance and real changes in one command? No — reconcile with terraform apply -refresh-only first, then generate and apply a normal plan for the actual changes. For more workflow fixes, see the Terraform guides.
Fixed it? Get 500 Terraform & 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.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 4Transport endpoint is not connected
- 5modprobe: FATAL: Module not found
- 6mount: wrong fs type, bad option, bad superblock
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.