{"record":{"id":"e4292152b8a437ed","repo":"hashicorp/terraform","slug":"remote-backend-doesn-t-support-s","errorCode":null,"errorMessage":"remote backend doesn't support %s","messagePattern":"remote backend doesn't support (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote/backend_plan.go","lineNumber":315,"sourceCode":"\t\tConfigurationVersion: cv,\n\t\tRefresh:              tfe.Bool(op.PlanRefresh),\n\t\tWorkspace:            w,\n\t}\n\n\tswitch op.PlanMode {\n\tcase plans.NormalMode:\n\t\t// okay, but we don't need to do anything special for this\n\tcase plans.RefreshOnlyMode:\n\t\trunOptions.RefreshOnly = tfe.Bool(true)\n\tcase plans.DestroyMode:\n\t\trunOptions.IsDestroy = tfe.Bool(true)\n\tdefault:\n\t\t// Shouldn't get here because we should update this for each new\n\t\t// plan mode we add, mapping it to the corresponding RunCreateOptions\n\t\t// field.\n\t\treturn nil, generalError(\n\t\t\t\"Invalid plan mode\",\n\t\t\tfmt.Errorf(\"remote backend doesn't support %s\", op.PlanMode),\n\t\t)\n\t}\n\n\tif len(op.Targets) != 0 {\n\t\trunOptions.TargetAddrs = make([]string, 0, len(op.Targets))\n\t\tfor _, addr := range op.Targets {\n\t\t\trunOptions.TargetAddrs = append(runOptions.TargetAddrs, addr.String())\n\t\t}\n\t}\n\n\tif len(op.ForceReplace) != 0 {\n\t\trunOptions.ReplaceAddrs = make([]string, 0, len(op.ForceReplace))\n\t\tfor _, addr := range op.ForceReplace {\n\t\t\trunOptions.ReplaceAddrs = append(runOptions.ReplaceAddrs, addr.String())\n\t\t}\n\t}\n\n\tr, err := b.client.Runs.Create(stopCtx, runOptions)","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote/backend_plan.go#L297-L333","documentation":"Thrown by the remote (Terraform Cloud/Enterprise) backend when the plan operation carries a plans.PlanMode value that the backend's switch statement in backend_plan.go:302-317 does not recognize. Only NormalMode, RefreshOnlyMode, and DestroyMode are mapped to RunCreateOptions fields; any other mode reaches the default branch and is treated as a programming/contract defect (the code comment at line 310 explicitly says it should be updated for each new plan mode). It signals that a new plan mode was added to Terraform core without a corresponding case in this remote backend.","triggerScenarios":"Calling the remote backend Operation with an op.PlanMode outside {plans.NormalMode, plans.RefreshOnlyMode, plans.DestroyMode}. Concretely this happens when Terraform core introduces a new plans.Mode constant and a CLI/wrapper passes it through to the remote backend's plan path before backend_plan.go is updated.","commonSituations":"A new Terraform release adds a plan mode (e.g. a future mode beyond refresh-only/destroy) but the user's remote backend code is from an older revision; or a custom fork/patch sets op.PlanMode to an experimental value. Mismatched Terraform CLI and backend library versions.","solutions":["Check the value of op.PlanMode at the call site and only forward NormalMode/RefreshOnlyMode/DestroyMode to the remote backend plan operation.","Upgrade the remote backend package (this file) to the version that adds a switch case for the new plan mode, mapping it to the matching tfe.RunCreateOptions field.","If you maintain a fork, add the missing case in backend_plan.go:302 and open/refresh upstream support for that plan mode.","Downgrade the caller to a Terraform core version whose plan modes are all covered by the remote backend."],"exampleFix":"// before\nrunOptions := buildRunOptions(op) // op.PlanMode == plans.SomeNewMode -> default branch errors\n\n// after\nswitch op.PlanMode {\ncase plans.NormalMode:\ncase plans.RefreshOnlyMode:\n    runOptions.RefreshOnly = tfe.Bool(true)\ncase plans.DestroyMode:\n    runOptions.IsDestroy = tfe.Bool(true)\ncase plans.SomeNewMode:\n    runOptions.SomeNewField = tfe.Bool(true) // add the case for the new mode\ndefault:\n    return nil, generalError(\"Invalid plan mode\", fmt.Errorf(\"remote backend doesn't support %s\", op.PlanMode))\n}","handlingStrategy":"validation","validationCode":"// Before invoking the remote backend plan operation, reject unsupported modes.\nfunc supportedPlanMode(m plans.Mode) error {\n    switch m {\n    case plans.NormalMode, plans.RefreshOnlyMode, plans.DestroyMode:\n        return nil\n    }\n    return fmt.Errorf(\"remote backend does not support plan mode %s; use a supported mode\", m)\n}\n\nif err := supportedPlanMode(op.PlanMode); err != nil { return err }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When adding a new plans.Mode constant, add the matching switch case in backend_plan.go:302 in the same change.","Add a unit test that enumerates all plans.Mode values and asserts each is handled by the switch.","Keep the Terraform CLI version aligned with the remote backend package version."],"tags":["backend","remote-backend","plan-mode","terraform-cloud","version-mismatch","internal"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}