{"record":{"id":"d5203a46111b3165","repo":"hashicorp/terraform","slug":"resource-s-has-an-unsupported-action-reason-s","errorCode":null,"errorMessage":"resource %s has an unsupported action reason %s","messagePattern":"resource (.+?) has an unsupported action reason (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/jsonplan/plan.go","lineNumber":648,"sourceCode":"\t\tr.ActionReason = ResourceInstanceDeleteBecauseNoResourceConfig\n\tcase plans.ResourceInstanceDeleteBecauseWrongRepetition:\n\t\tr.ActionReason = ResourceInstanceDeleteBecauseWrongRepetition\n\tcase plans.ResourceInstanceDeleteBecauseCountIndex:\n\t\tr.ActionReason = ResourceInstanceDeleteBecauseCountIndex\n\tcase plans.ResourceInstanceDeleteBecauseEachKey:\n\t\tr.ActionReason = ResourceInstanceDeleteBecauseEachKey\n\tcase plans.ResourceInstanceDeleteBecauseNoModule:\n\t\tr.ActionReason = ResourceInstanceDeleteBecauseNoModule\n\tcase plans.ResourceInstanceDeleteBecauseNoMoveTarget:\n\t\tr.ActionReason = ResourceInstanceDeleteBecauseNoMoveTarget\n\tcase plans.ResourceInstanceReadBecauseConfigUnknown:\n\t\tr.ActionReason = ResourceInstanceReadBecauseConfigUnknown\n\tcase plans.ResourceInstanceReadBecauseDependencyPending:\n\t\tr.ActionReason = ResourceInstanceReadBecauseDependencyPending\n\tcase plans.ResourceInstanceReadBecauseCheckNested:\n\t\tr.ActionReason = ResourceInstanceReadBecauseCheckNested\n\tdefault:\n\t\treturn r, fmt.Errorf(\"resource %s has an unsupported action reason %s\", r.Address, rc.ActionReason)\n\t}\n\n\treturn r, nil\n}\n\n// MarshalDeferredResourceChanges converts the provided internal representation\n// of DeferredResourceInstanceChangeSrc objects into the public structured JSON\n// changes.\n// This is public to make testing easier.\nfunc MarshalDeferredResourceChanges(resources []*plans.DeferredResourceInstanceChangeSrc, schemas *terraform.Schemas) ([]DeferredResourceChange, error) {\n\tvar ret []DeferredResourceChange\n\n\tvar sortedResources []*plans.DeferredResourceInstanceChangeSrc\n\tsortedResources = append(sortedResources, resources...)\n\tsort.Slice(sortedResources, func(i, j int) bool {\n\t\tif !sortedResources[i].ChangeSrc.Addr.Equal(sortedResources[j].ChangeSrc.Addr) {\n\t\t\treturn sortedResources[i].ChangeSrc.Addr.Less(sortedResources[j].ChangeSrc.Addr)\n\t\t}","sourceCodeStart":630,"sourceCodeEnd":666,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/command/jsonplan/plan.go#L630-L666","documentation":"Thrown by jsonplan.marshalResource when mapping the internal plans.ResourceInstanceChangeReason enum to its JSON string form. The switch lists a fixed set of reasons (replace-because-cannot-update, tainted, delete-because-*, read-because-*). A planned change whose ActionReason is not one of those mapped constants falls through to default. This usually means the plans package grew a new reason constant that the JSON layer was not updated to recognize.","triggerScenarios":"A new plans.ResourceInstanceChange*Reason constant is added without a matching case; a provider/SDK path sets an ActionReason value outside the documented set; an internal regression assigns a zero/garbage reason.","commonSituations":"Cross-binary plan consumption (plan produced by a newer Terraform, shown by an older one); running an unreleased/dev build whose plans and jsonplan packages are out of sync.","solutions":["Use the same Terraform version to produce and consume the plan.","Upgrade Terraform so the jsonplan switch covers all current ActionReason values.","If extending Terraform, add the missing case mapping in internal/command/jsonplan/plan.go near line 648.","Report the unmapped reason string from the error message upstream."],"exampleFix":"// before\ndefault:\n\treturn r, fmt.Errorf(\"resource %s has an unsupported action reason %s\", r.Address, rc.ActionReason)\n\n// after (map the new constant to its JSON string)\ncase plans.ResourceInstanceReplaceBecauseGreaterThan:\n\tr.ActionReason = ResourceInstanceReplaceBecauseGreaterThan","handlingStrategy":"validation","validationCode":"// Guard before marshaling: skip or map only known action reasons.\nvar knownReasons = map[plans.ResourceInstanceChangeReason]bool{\n    plans.ResourceInstanceChangeNoReason: true,\n    plans.ResourceInstanceReplaceBecauseCannotUpdate: true,\n    plans.ResourceInstanceReplaceBecauseTainted: true,\n    plans.ResourceInstanceReplaceByRequest: true,\n    plans.ResourceInstanceDeleteBecauseNoResource: true,\n    plans.ResourceInstanceDeleteBecauseWrongRepetition: true,\n    plans.ResourceInstanceDeleteBecauseCountIndex: true,\n    plans.ResourceInstanceDeleteBecauseEachKey: true,\n    plans.ResourceInstanceDeleteBecauseNoModule: true,\n    plans.ResourceInstanceDeleteBecauseNoMoveTarget: true,\n    plans.ResourceInstanceReadBecauseConfigUnknown: true,\n    plans.ResourceInstanceReadBecauseDependencyPending: true,\n    plans.ResourceInstanceReadBecauseCheckNested: true,\n}\n\nif !knownReasons[rc.ActionReason] {\n    return fmt.Errorf(\"skipping change with unmapped action reason %d\", rc.ActionReason)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep plans and jsonplan packages in sync when adding a ResourceInstanceChangeReason constant.","Consume a plan with the same Terraform version that produced it.","Treat a new reason string as a signal to update the JSON mapping, not an ignorable default."],"tags":["terraform","json-plan","action-reason","enum-exhaustiveness","serialization"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}