hashicorp/terraform · error

Error copying state from the previous %q backend to the newl

Error message

Error copying state from the previous %q backend to the newly configured
%q backend:
    %s

The state in the previous backend remains intact and unmodified. Please resolve
the error above and try again.

What it means

Returned by backendMigrateState_s_s when statemgr.Migrate(destinationState, sourceState) at line 456 fails — the confirmed copy/merge of source state into the destination state manager errored. The message errBackendStateCopy stresses the SOURCE remains intact (migration is a copy), so re-running is safe once the destination issue is fixed.

Source

Thrown at internal/command/meta_backend_migrate.go:457

		// Confirm with the user whether we want to copy state over
		confirm, err := confirmFunc(sourceState, destinationState, opts)
		if err != nil {
			log.Print("[TRACE] backendMigrateState: error reading input, so aborting migration")
			return err
		}
		if !confirm {
			log.Print("[TRACE] backendMigrateState: user cancelled at confirmation prompt, so aborting migration")
			return nil
		}
	}

	// Confirmed! We'll have the statemgr package handle the migration, which
	// includes preserving any lineage/serial information where possible, if
	// both managers support such metadata.
	log.Print("[TRACE] backendMigrateState: migration confirmed, so migrating")
	if err := statemgr.Migrate(destinationState, sourceState); err != nil {
		return fmt.Errorf(strings.TrimSpace(errBackendStateCopy),
			opts.SourceType, opts.DestinationType, err)
	}
	// The backend is currently handled before providers are installed during init,
	// so requiring schemas here could lead to a catch-22 where it requires some manual
	// intervention to proceed far enough for provider installation. To avoid this,
	// when migrating to HCP Terraform backend, the initial JSON varient of state won't be generated and stored.
	if err := destinationState.PersistState(nil); err != nil {
		return fmt.Errorf(strings.TrimSpace(errBackendStateCopy),
			opts.SourceType, opts.DestinationType, err)
	}

	// And we're done.
	return nil
}

func (m *Meta) backendMigrateEmptyConfirm(source, destination statemgr.Full, opts *backendMigrateOpts) (bool, error) {
	var inputOpts *terraform.InputOpts
	if opts.DestinationType == "cloud" {

View on GitHub (pinned to c9def3e214)

Solutions

  1. Read the inner %s for the Migrate-level failure (write error, lineage conflict, size limit).
  2. Fix destination write permissions / capacity, then re-run 'terraform init' — source is unchanged.
  3. If lineage conflicts, decide which state is authoritative and manually 'terraform state push' it after clearing the destination.
  4. For size limits (e.g., remote backend), reduce state size or use a backend with higher limits.
  5. Check destination backend logs for the rejected write payload.

Example fix

// before: destination write denied mid-Migrate
//   Error copying state: ... AccessDenied on PutObject
// after: grant write and retry (source intact)
//   (attach s3:PutObject policy)
//   terraform init
Defensive patterns

Strategy: validation

Validate before calling

# Verify destination write access + lineage compatibility before migrating.
aws s3api put-object --bucket "$TF_DST_BUCKET" --key "$TF_DST_KEY.migrationprobe" --body /dev/null >/dev/null \
  || { echo 'destination not writable'; exit 1; }
aws s3 rm "s3://$TF_DST_BUCKET/$TF_DST_KEY.migrationprobe"
# Compare lineage if both states present:
terraform state pull -state=/tmp/src.tfstate
# inspect .lineage vs destination's lineage to anticipate conflicts

Try / catch

# Detect Migrate-level copy failure; source is intact so a retry is safe after fixing dst.
terraform init 2>/tmp/init.err || rc=$?
if grep -q 'Error copying state' /tmp/init.err; then
  echo 'State copy failed — source intact. Fix destination write/lineage and retry.' >&2
fi
exit ${rc:-0}

Prevention

When it happens

Trigger: After confirmation and locking, the actual state-copy operation (which preserves lineage/serial) fails: destination write failure, lineage conflict that Migrate can't reconcile, state schema incompatibility between source and destination managers. Wrapped at line 457-458.

Common situations: Destination backend write permission missing; destination state has a conflicting lineage with the source (both non-empty with different lineage — though the non-empty confirm prompt usually precedes this); large state exceeding destination size limits; remote backend rejecting the state version payload.

Related errors


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