hashicorp/terraform · critical

expected exactly one provider requirement for the destinatio

Error message

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

What it means

This panic fires at the top of getSingleProvider when len(reqs) != 1. getSingleProvider downloads exactly one state-store provider (source or destination) during migration, so the caller must hand it a Requirements map of size 1; any other size is a programming contract violation.

Source

Thrown at internal/command/state_migrate.go:503

	// 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
// - Need to keep the locks separate for source and destination providers; destination providers are added to the dependency lock file.
func (c *StateMigrateCommand) getSingleProvider(ctx context.Context, stateStore *configs.StateStore, reqs providerreqs.Requirements, locks *depsfile.Locks, upgrade bool, location string, view views.StateMigrate) (output bool, resultingLock *depsfile.Locks, trust ProviderTrust, authResult *getproviders.PackageAuthenticationResult, diags tfdiags.Diagnostics) {
	ctx, span := tracer.Start(ctx, "install state migration "+location+" provider")
	defer span.End()

	// We expect to download only one provider
	if len(reqs) != 1 {
		panic(fmt.Sprintf("expected exactly one provider requirement for the destination state store provider, got %d", len(reqs)))
	}

	// Check for legacy provider addresses.
	for providerAddr := range reqs {
		if providerAddr.IsLegacy() {
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				"Invalid legacy provider address",
				fmt.Sprintf(
					"This configuration or its associated state refers to the unqualified provider %q.\n\nYou must complete the Terraform 0.13 upgrade process before upgrading to later versions.",
					providerAddr.Type,
				),
			))
		}
	}
	if diags.HasErrors() {
		return false, nil, Invalid, nil, diags
	}

View on GitHub (pinned to d32a084675)

Solutions

  1. Audit every caller of getSingleProvider — each must pass a Requirements map containing exactly one provider entry (the source or the destination).
  2. Re-run state_migrate_test.go to confirm the existing source/destination flows still pass single-element maps.
  3. If you need multi-provider download, write a new helper rather than loosening this invariant.

Example fix

// before — caller passes full config requirements
reqs := allConfigRequirements // size N
output, lock, trust, auth, diags := c.getSingleProvider(ctx, store, reqs, locks, upgrade, loc, view)
// after — caller passes the filtered single provider
single := providerreqs.Requirements{srcProvider: reqs[srcProvider]}
output, lock, trust, auth, diags := c.getSingleProvider(ctx, store, single, locks, upgrade, loc, view)
Defensive patterns

Strategy: validation

Validate before calling

// Enforce the single-element contract at the call boundary.
if len(reqs) != 1 {
  return fmt.Errorf("getSingleProvider requires exactly 1 requirement, got %d", len(reqs))
}

Type guard

// n/a — length check

Prevention

When it happens

Trigger: Called twice during state migration (once for source, once for destination provider). Panics if getDestinationStateStoreProviderRequirements or the source-side equivalent returns 0 or >1 requirements. Because errors 1003/1004 already guard the destination path, this is most likely reached via the source-side caller passing the wrong Requirements object, or a future caller misusing the helper.

Common situations: A maintainer adds a new caller of getSingleProvider that builds a multi-provider Requirements map (e.g. reusing the full config requirements instead of the filtered single-provider map). Not user-triggerable through normal CLI flags.

Related errors


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