{"record":{"id":"02ac1c1de9dcf8e1","repo":"hashicorp/terraform","slug":"failed-to-marshal-index-step-key-v-s","errorCode":null,"errorMessage":"Failed to marshal index step key %#v: %s","messagePattern":"Failed to marshal index step key %#v: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/jsonplan/plan.go","lineNumber":1072,"sourceCode":"\tfor _, path := range pathList {\n\t\tjsonPath, err := encodePath(path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tjsonPaths = append(jsonPaths, jsonPath)\n\t}\n\n\treturn json.Marshal(jsonPaths)\n}\n\nfunc encodePath(path cty.Path) (json.RawMessage, error) {\n\tsteps := make([]json.RawMessage, 0, len(path))\n\tfor _, step := range path {\n\t\tswitch s := step.(type) {\n\t\tcase cty.IndexStep:\n\t\t\tkey, err := ctyjson.Marshal(s.Key, s.Key.Type())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Failed to marshal index step key %#v: %s\", s.Key, err)\n\t\t\t}\n\t\t\tsteps = append(steps, key)\n\t\tcase cty.GetAttrStep:\n\t\t\tname, err := json.Marshal(s.Name)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Failed to marshal get attr step name %#v: %s\", s.Name, err)\n\t\t\t}\n\t\t\tsteps = append(steps, name)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"Unsupported path step %#v (%t)\", step, step)\n\t\t}\n\t}\n\treturn json.Marshal(steps)\n}\n","sourceCodeStart":1054,"sourceCodeEnd":1087,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/command/jsonplan/plan.go#L1054-L1087","documentation":"Thrown by jsonplan.encodePath when converting a cty.Path (used for sensitive-attribute paths and diff paths) to JSON. For a cty.IndexStep, the key is encoded with ctyjson.Marshal(s.Key, s.Key.Type()). If that marshal fails (e.g. the key is a null/unknown value, or has an exotic cty type ctyjson cannot encode), the error wraps the underlying failure. The plan JSON then cannot represent which indexed element is sensitive/changed.","triggerScenarios":"A resource uses for_each/count and a sensitive value sits at an indexed path whose key is null, unknown, or of a type ctyjson rejects; a provider emits an attribute path with an unusual index key type.","commonSituations":"for_each over a map with sensitive nested attributes; plan output (-json) for a configuration combining dynamic blocks with sensitive markers; provider schema returning paths with object/tuple-typed index keys.","solutions":["Upgrade Terraform/ctyjson; newer versions handle more key types.","Simplify the index key type (use strings/numbers rather than complex objects as for_each keys).","Reduce sensitivity markers on dynamically-indexed attributes to isolate the offending path.","Reproduce with `terraform plan -json` and report the key type (%#v) shown in the message upstream."],"exampleFix":"// before\nkey, err := ctyjson.Marshal(s.Key, s.Key.Type())\nif err != nil {\n\treturn nil, fmt.Errorf(\"Failed to marshal index step key %#v: %s\", s.Key, err)\n}\n\n// after (handle null/unknown keys defensively before encoding)\nif !s.Key.IsKnown() || s.Key.IsNull() {\n\treturn nil, fmt.Errorf(\"cannot encode path with unknown/null index key: %#v\", s.Key)\n}\nkey, err := ctyjson.Marshal(s.Key, s.Key.Type())","handlingStrategy":"try-catch","validationCode":"// Reject null/unknown index keys before they reach ctyjson.\nfunc encodablePath(p cty.Path) error {\n    for _, step := range p {\n        if idx, ok := step.(cty.IndexStep); ok {\n            if !idx.Key.IsKnown() || idx.Key.IsNull() {\n                return fmt.Errorf(\"path contains unencodable index key: %#v\", idx.Key)\n            }\n        }\n    }\n    return nil\n}","typeGuard":"func isKnownIndexStep(s cty.PathStep) bool {\n    idx, ok := s.(cty.IndexStep)\n    if !ok {\n        return true\n    }\n    return idx.Key.IsKnown() && !idx.Key.IsNull()\n}","tryCatchPattern":"raw, err := jsonplan.MarshalChanges(changes)\nif err != nil && strings.Contains(err.Error(), \"Failed to marshal index step key\") {\n    // surface a clearer error pointing at the for_each/count config\n    return fmt.Errorf(\"cannot JSON-encode plan: a sensitive/changed value sits at an index path with an unencodable key; simplify for_each keys: %w\", err)\n}","preventionTips":["Prefer string/number for_each keys over complex object keys.","Keep Terraform and ctyjson current; later versions encode more key types.","When a sensitive value is nested under a dynamic index, test `terraform plan -json` early to catch encoding failures."],"tags":["terraform","json-plan","cty","path-encoding","sensitive-values","for-each"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}