hashicorp/terraform · error

Migrating state from HCP Terraform or Terraform Enterprise…

Error message

Migrating state from HCP Terraform or Terraform Enterprise to another %s is 
yet implemented.

Please use the API to do this: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/state-versions

What it means

When the source backend is HCP Terraform / Terraform Enterprise (cloud.Cloud) and the destination is a non-TFC backend type, Terraform does not implement an automated migration path. The backendMigrateTFC function detects this combination (sourceTFC && !destinationTFC) and returns this error directing users to the HCP Terraform state-versions API. This is a deliberate limitation, not a transient failure.

Solutions

  1. Use `terraform state pull` to download the current state, then configure the new backend and run `terraform state push`
  2. Use the HCP Terraform state-versions API (https://developer.hashicorp.com/terraform/cloud-docs/api-docs/state-versions) to export state for each workspace
  3. Manually create the state file in the new backend after downloading it
  4. Use `terraform init -reconfigure` after manually placing state in the destination backend to skip automated migration

Example fix

# before: switching from cloud to s3 backend triggers this error
terraform init
# after: manual migration path
terraform state pull > terraform.tfstate
# edit backend config to s3, then:
terraform init -reconfigure
terraform state push terraform.tfstate
Defensive patterns

Strategy: validation

Validate before calling

// Detect cloud->non-cloud migration before running init
// Shell: check if config changed from cloud{} to backend "x"
//   grep -q 'cloud {' old_config.tf && grep -q 'backend "' new_config.tf && echo "UNSUPPORTED_MIGRATION"
//
// Go-level: inspect source/destination backend types
func isUnsupportedMigration(source, dest backend.Backend) bool {
    _, sourceIsCloud := source.(*cloud.Cloud)
    _, destIsCloud := dest.(*cloud.Cloud)
    return sourceIsCloud && !destIsCloud
}

Type guard

// Check if a backend is a cloud backend
func isCloudBackend(b backend.Backend) bool {
    _, ok := b.(*cloud.Cloud)
    return ok
}

Prevention

When it happens

Trigger: Changing the terraform configuration from a `cloud {}` block to a different backend type (e.g., `backend "s3"`, `backend "local"`, `backend "http"`) and running `terraform init`. The source is identified as cloud.Cloud but the destination is not.

Common situations: Moving infrastructure management from HCP Terraform to a self-hosted backend, consolidating state into S3, switching from managed to local for testing, or organizational restructuring that changes where state is managed.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/043ef0677e069ca1. Report an issue: GitHub.

Appendix: source

Thrown at internal/command/meta_backend_migrate.go:619

	cloudBackendDestination, destinationTFC := opts.Destination.(*cloud.Cloud)

	sourceWorkspaces, sourceSingleState, err := retrieveWorkspaces(opts.Source, opts.SourceType)
	if err != nil {
		return err
	}
	// to be used below, not yet implemented
	// destinationWorkspaces, destinationSingleState
	_, _, err = retrieveWorkspaces(opts.Destination, opts.SourceType)
	if err != nil {
		return err
	}

	// from HCP Terraform to non-TFC backend
	if sourceTFC && !destinationTFC {
		dstWord := backendHumanName(opts.Destination)
		// From HCP Terraform to another backend. This is not yet implemented, and
		// we recommend people to use the HCP Terraform API.
		return fmt.Errorf(
			strings.TrimSpace(errTFCMigrateNotYetImplemented), dstWord)
	}

	// Everything below, by the above two conditionals, now assumes that the
	// destination is always HCP Terraform.
	sourceSingle := sourceSingleState || (len(sourceWorkspaces) == 1)
	if sourceSingle {
		if cloudBackendDestination.WorkspaceMapping.Strategy() == cloud.WorkspaceNameStrategy {
			// If we know the name via WorkspaceNameStrategy, then set the
			// destinationWorkspace to the new Name and skip the user prompt. Here the
			// destinationWorkspace is not set to `default` thereby we will create it
			// in HCP Terraform if it does not exist.
			opts.destinationWorkspace = cloudBackendDestination.WorkspaceMapping.Name
		}

		currentWorkspace, err := m.Workspace()
		if err != nil {
			return err

View on GitHub (pinned to d32a084675)