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
- Audit every caller of getSingleProvider — each must pass a Requirements map containing exactly one provider entry (the source or the destination).
- Re-run state_migrate_test.go to confirm the existing source/destination flows still pass single-element maps.
- 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
- Document the size-1 invariant on getSingleProvider's godoc.
- Add a unit test asserting every caller passes a 1-element map.
- Prefer returning (Requirements, error) over panic for internal helpers.
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
- expected one provider requirement for the destination state
- expected exactly one provider requirement for the destinatio
- failed to encode main index: %s
- failed to encode version index: %s
- backendDiags.Err()
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/db0e21b282f54915.
Report an issue: GitHub.