Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 8 min read

Terraform Error: 'moved' block invalid reference (both endpoints exist)

Quick answer

Fix Terraform 'moved' block errors like 'Unable to move ... both refer to existing objects' and 'Invalid moved block address': point from an old, gone address to a new one.

  • #terraform
  • #iac
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Exact Error Message


│ Error: Unable to move resource instance

│   with aws_instance.web,
│   on moved.tf line 1:
│    1: moved {

│ Both aws_instance.app and aws_instance.web refer to existing objects in
│ the configuration, so Terraform cannot move one to the other.

Other variants from the same block:

│ Error: Invalid "moved" block

│ The "from" and "to" addresses must be different, but both refer to
│ aws_instance.web.
│ Error: Ambiguous move statements

│ A chain of move statements leads to aws_instance.web, but that address
│ appears as the "to" of more than one move.

What It Means

A moved block tells Terraform that a resource previously tracked at one address (from) should now be tracked at another (to), so state follows a rename or refactor without a destroy/create. The rule is that the from address must no longer exist in configuration, and the to address must be the new, declared resource.

The most common error appears when both addresses still have resource blocks in your config. Terraform will not silently merge two live resources, so it refuses the move. Other variants fire when from and to are identical, or when multiple moved blocks converge on the same destination.

Common Causes

  • You added a moved block but forgot to delete the old resource "..." block, so both endpoints still exist.
  • from and to are the same address (copy-paste, or an incomplete edit).
  • Two moved blocks both target the same to address, creating an ambiguous chain.
  • The to address does not match any declared resource (typo, wrong index, wrong module path).
  • Moving into a for_each/count instance with the wrong key/index syntax ([0] vs ["a"]).
  • A module-boundary move where the module. prefix is missing or wrong.

Diagnostic Commands

List what actually exists in state so you can see which address is real:

terraform state list

Show the concrete address you intend to move away from:

terraform state show aws_instance.app

Run a plan — moved blocks are resolved during plan, and the output shows moves as # ... has moved to ...:

terraform plan

Validate to catch malformed addresses before planning:

terraform validate

Step-by-Step Resolution

  1. Decide the true old and new addresses. The from should be the address that no longer has a resource block; the to should be the one that does.

  2. Delete the old resource block. Keep only the new resource plus the moved block:

# old block removed entirely — do NOT leave it in the file

resource "aws_instance" "web" {
  ami           = "ami-0abcd1234ef567890"
  instance_type = "t3.micro"
}

moved {
  from = aws_instance.app
  to   = aws_instance.web
}
  1. Make sure from and to differ. If they are identical, the move is a no-op and must be removed or corrected.

  2. For instances under count/for_each, use exact index/key syntax:

moved {
  from = aws_instance.web
  to   = aws_instance.web["primary"]
}
  1. For a move across a module boundary, include the full module. path on both sides:
moved {
  from = aws_s3_bucket.logs
  to   = module.logging.aws_s3_bucket.logs
}
  1. Resolve ambiguous chains: ensure each to address is targeted by only one moved block. Then plan and confirm the move shows as a state move, not a destroy/create:
terraform plan
  # aws_instance.app has moved to aws_instance.web
  1. Once the move has been applied and state is updated, you may delete the moved block in a later change.

Prevention

  • When renaming a resource, delete the old block in the same commit as you add the moved block.
  • Keep from (gone from config) and to (declared in config) clearly distinct; never point a move at itself.
  • Use exact for_each keys and count indexes in move addresses.
  • Include the full module. path when moving resources into or out of modules.
  • Run terraform plan after any refactor to confirm moves resolve as state moves rather than replacements.
  • Resource instance managed by newer provider version — unrelated state/version mismatch, not a move.
  • removed block ... still in configuration — the removed block sibling error when a resource is both removed and declared.
  • Invalid resource instance key — wrong for_each/count key inside a move address.
  • Reference to undeclared resource — the to address names a resource that does not exist.

Frequently Asked Questions

Why does Terraform say both addresses exist? You left the old resource block in the config alongside the new one. A moved block requires the from address to be gone from configuration — delete the old block.

Do I keep the moved block forever? No. Once the move has been applied and state reflects the new address, you can safely remove the moved block in a subsequent change.

How do I move a resource into a module? Use the full path on the to side, e.g. to = module.logging.aws_s3_bucket.logs, and remove the standalone resource from the root. A Terraform prompt can help you draft the exact addresses.

What causes “ambiguous move statements”? Two moved blocks pointing at the same to address, or a chain that converges. Ensure each destination is targeted by only one move. For more state-refactor fixes, see the Terraform guides.

Free download · 368-page PDF

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.