hashicorp/terraform · critical

State migration failed: %w

Error message

State migration failed: %w

What it means

The top-level failure returned by `terraform state migrate` when Meta.backendMigrateState(migrateOpts) returns an error. backendMigrateState is the orchestrator that copies state from the source backend to the destination backend; any failure in locking, reading source state, writing destination state, or backend-specific transfer surfaces here wrapped as %w. The preceding view.Log already printed StateMigrationFailureMessage naming source and destination.

Source

Thrown at internal/command/state_migrate.go:336

		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Unknown migration destination",
			"No configuration was provided for where to migrate the state to. Please ensure that a file with a .tf extension is present and contains valid state_store or backend configuration inside the terraform block.",
		))
	}

	// present all errors from above together so user can fix them all at once
	if diags.HasErrors() {
		view.Diagnostics(diags)
		return 1
	}

	view.Log(views.StateMigrationStartMessage, source, destination)

	// Perform the migration from source to destination
	err := c.Meta.backendMigrateState(migrateOpts)
	if err != nil {
		diags = diags.Append(fmt.Errorf("State migration failed: %w", err))
		view.Diagnostics(diags)
		view.Log(views.StateMigrationFailureMessage, source, destination)
		return 1
	}

	// After a successful migration to a state store, we must make sure the dependency lock file contains the
	// details of the destination state store provider.
	if rootMod.StateStore != nil {
		originalLocks, originalLockDiags := c.lockedDependencies()
		diags = diags.Append(originalLockDiags)
		if originalLockDiags.HasErrors() {
			view.Diagnostics(diags)
			return 1
		}

		// Get the combination of locks
		//
		// Take the lock from the destination provider download and add in the original locks from the dependency lock file.

View on GitHub (pinned to c9def3e214)

Solutions

  1. Read the wrapped %w — it states the concrete failure (lock, auth, write).
  2. If the lock is stuck from a crashed process, force-unlock: `terraform force-unlock <LOCK_ID>`.
  3. Verify destination backend credentials and write permissions, then re-run `terraform state migrate`.
  4. Ensure no other Terraform process is operating on the same state, then retry.

Example fix

# before - migrating while a stale lock remains
terraform state migrate
# State migration failed: Failed to lock state: lock info...

# after
terraform force-unlock <LOCK_ID>
terraform state migrate
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: confirm source lock is free and destination is writable.
// (Backend-specific; example for local lock file)
func lockIsFree(path string) bool {
    _, err := os.Stat(path + ".tflock")
    return os.IsNotExist(err)
}

Try / catch

// retry transient migration failures (lock contention / network) with backoff
for attempt := 0; attempt < 3; attempt++ {
    if err := backendMigrateState(opts); err != nil {
        if isTransient(err) {
            time.Sleep(backoff(attempt))
            continue
        }
        return err
    }
    break
}

Prevention

When it happens

Trigger: Running `terraform state migrate` where the actual state transfer fails: source backend cannot acquire/release its state lock, destination backend write/lock fails, network/auth error to S3/cloud/HTTP backend, or incompatible backend implementations.

Common situations: Migrating to S3 without bucket write/IAM permissions; state lock held by another running operation; stale lock from a crashed run; HCP/TFE token expired or lacking workspace access; cross-region bucket misconfiguration; concurrent applies holding the lock.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/9039c543bdf413c8. Report an issue: GitHub.