hashicorp/terraform · critical

expected exactly one provider requirement for the destinatio

Error message

expected exactly one provider requirement for the destination state store provider %q, got %d

What it means

This panic fires at the end of getDestinationStateStoreProviderRequirements when the filtered requirements map does not contain exactly one entry. The function expects precisely one provider requirement matching the destination state_store provider; zero means no matching required_providers entry was found, two+ means duplicate entries for the same provider address.

Source

Thrown at internal/command/state_migrate.go:477

	for _, providerReq := range configReqs.RequiredProviders {
		if providerReq.Type.Equals(provider) {
			con, err := providerreqs.ParseVersionConstraints(providerReq.Requirement.Required.String())
			if err != nil {
				diags = diags.Append(&hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid version constraint syntax for state store provider",
					// The errors returned by ParseVersionConstraint already include
					// the section of input that was incorrect, so we don't need to
					// include that here.
					Detail:  fmt.Sprintf("Incorrect version constraint syntax: %s.", err.Error()),
					Subject: providerReq.Requirement.DeclRange.Ptr(),
				})
			}
			req[providerReq.Type] = con
		}
	}
	if len(req) != 1 {
		panic(fmt.Sprintf("expected exactly one provider requirement for the destination state store provider %q, got %d", provider, len(req)))
	}

	return req, diags
}

// saveDependencyLockFile overwrites the contents of the dependency lock file.
func (c *StateMigrateCommand) saveDependencyLockFile(previousLocks, newLocks *depsfile.Locks, view views.ProviderLockingLogger) (output bool, diags tfdiags.Diagnostics) {
	// The state migrate command does not support the -lockfile=readonly flag
	// This flag is specific to the init command, and can only take "" or "readonly" as values.
	// As state migrate doesn't take this flag, we can safely set it to "" here.
	flagLockfile := ""

	return c.Meta.saveDependencyLockFile(previousLocks, newLocks, c.incompleteProviders, flagLockfile, view)
}

// getSingleProvider is used to download the source and/or destination state store providers during a state migration.
// Download of the up to 2 providers is kept separate due to:
// - Potential for downloading different versions of the same provider

View on GitHub (pinned to d32a084675)

Solutions

  1. Verify the `provider = "..."` value in the state_store block exactly matches the `source = "..."` in required_providers (full registry namespace + type).
  2. Remove any duplicate required_providers entries for the same provider source; consolidate to one declaration with one version constraint.
  3. Run `terraform init` to confirm Terraform resolves exactly one requirement for the state-store provider.

Example fix

// before — duplicate required_providers for the same source
required_providers {
  a = { source = "registry.terraform.io/hashicorp/tfstate-mysql", version = ">= 0.1" }
  b = { source = "registry.terraform.io/hashicorp/tfstate-mysql", version = ">= 0.2" }
}
// after — single canonical declaration
required_providers {
  tfstate-mysql = {
    source  = "registry.terraform.io/hashicorp/tfstate-mysql"
    version = ">= 0.2"
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Caller-side guard: confirm exactly one matching requirement exists.
matches := 0
for _, pr := range configReqs.RequiredProviders {
  if pr.Type.Equals(provider) { matches++ }
}
if matches != 1 {
  return fmt.Errorf("need exactly one required_providers entry for %q, found %d", provider, matches)
}

Type guard

// n/a

Prevention

When it happens

Trigger: State migration setup: after iterating configReqs.RequiredProviders and keeping only entries whose Type equals the destination provider, len(req) != 1. Zero matches = the required_providers block names a different provider than the state_store block; >1 = the same provider source is declared twice (e.g. two required_providers entries with the same source but different local names).

Common situations: A config with a state_store block pointing at provider A but a required_providers entry only for provider B (mismatch). Or a copy-paste required_providers block declaring the same source twice under different local names.

Related errors


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