hashicorp/terraform · error
doesn't support
Error message
%s doesn't support %s
What it means
Thrown in the default branch of the switch over op.PlanMode in the cloud plan-creation path. The switch handles NormalMode, RefreshOnlyMode, and DestroyMode explicitly; any other value is rejected because there is no mapping to a tfe.RunCreateOptions field. The comment explicitly states this is a developer guard: it should be updated whenever a new plan mode is added.
Solutions
- This is almost always an internal Terraform defect — report it to the Terraform team with the op.PlanMode value from the message.
- If running a fork, add a case for the new plan mode mapping it to the correct RunCreateOptions field.
- Downgrade to a stable Terraform release where the cloud backend and core plan modes are in sync.
- Avoid the unsupported flag combination that surfaces the new mode until the backend is updated.
Example fix
// before
case plans.DestroyMode:
runOptions.IsDestroy = tfe.Bool(true)
default:
return nil, b.generalError("Invalid plan mode", ...)
// after - add the missing mode mapping
case plans.DestroyMode:
runOptions.IsDestroy = tfe.Bool(true)
case plans.NewMode:
runOptions.NewField = tfe.Bool(true)
default:
return nil, b.generalError("Invalid plan mode", ...) Defensive patterns
Strategy: validation
Validate before calling
// Guard at the boundary before constructing the operation.
switch op.PlanMode {
case plans.NormalMode, plans.RefreshOnlyMode, plans.DestroyMode:
default:
return fmt.Errorf("unsupported plan mode: %v", op.PlanMode)
} Type guard
// Narrow plan mode to supported values.
func isSupportedPlanMode(m plans.Mode) bool {
switch m {
case plans.NormalMode, plans.RefreshOnlyMode, plans.DestroyMode:
return true
}
return false
} Prevention
- When adding a new plans.Mode constant, update the cloud backend switch in the same change.
- Use released Terraform versions; avoid forks that inject arbitrary plan modes.
- Add a unit test asserting every plans.Mode has a cloud-backend case.
When it happens
Trigger: A new plans.Mode constant was added to Terraform core without a corresponding case in this switch. This is an internal Terraform bug, not a user config error — op.PlanMode is set by the CLI flags (-destroy, -refresh-only), which cannot produce an unmapped value through normal use.
Common situations: Running a custom/development Terraform build that added a plan mode without updating the cloud backend; a plugin or fork that injects an unexpected PlanMode value; version skew between terraform-core and the cloud backend package.
Related errors
- Invalid Task stage status
- confirmFunc must not be nil
- errStateStoreInitDiag requires a non-nil reason argument
- init (determineIfProviderTrusted): unexpected provider…
- invalid collection length
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/0184987133e45f6e.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloud/backend_plan.go:164
Workspace: w,
AutoApply: tfe.Bool(op.AutoApprove),
SavePlan: tfe.Bool(op.PlanOutPath != ""),
}
switch op.PlanMode {
case plans.NormalMode:
// okay, but we don't need to do anything special for this
case plans.RefreshOnlyMode:
runOptions.RefreshOnly = tfe.Bool(true)
case plans.DestroyMode:
runOptions.IsDestroy = tfe.Bool(true)
default:
// Shouldn't get here because we should update this for each new
// plan mode we add, mapping it to the corresponding RunCreateOptions
// field.
return nil, b.generalError(
"Invalid plan mode",
fmt.Errorf("%s doesn't support %s", b.appName, op.PlanMode),
)
}
if len(op.Targets) != 0 {
runOptions.TargetAddrs = make([]string, 0, len(op.Targets))
for _, addr := range op.Targets {
runOptions.TargetAddrs = append(runOptions.TargetAddrs, addr.String())
}
}
if len(op.ActionTargets) != 0 {
if len(op.ActionTargets) > 1 {
// For now, we only support a single action from the command line.
// We've future proofed the API and inputs so we can send multiple
// but versions of Terraform will enforce this both here, and
// on the other side.
//
// It shouldn't actually be possible to reach here anyway - we'reView on GitHub (pinned to d32a084675)