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
- Use `terraform state pull` to download the current state, then configure the new backend and run `terraform state push`
- Use the HCP Terraform state-versions API (https://developer.hashicorp.com/terraform/cloud-docs/api-docs/state-versions) to export state for each workspace
- Manually create the state file in the new backend after downloading it
- 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
- Never switch directly from cloud{} to a non-cloud backend expecting automated migration — it is not implemented
- Plan cloud-to-non-cloud migrations as a manual state pull/push workflow
- Document the manual migration procedure for teams that may need to leave HCP Terraform
- Test the pull/push workflow in a non-production environment first
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
- Error migrating the workspace %[1]q from the previous %[2]q…
- Error asking for state migration action
- Error copying state from the previous %[1]q %[2]s to the…
- Error creating temporary directory
- Error inspecting states in the
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 errView on GitHub (pinned to d32a084675)