diff --git a/.changes/v1.14/BUG FIXES-20260311-163804.yaml b/.changes/v1.14/BUG FIXES-20260311-163804.yaml new file mode 100644 index 0000000000..5c0ae36b6f --- /dev/null +++ b/.changes/v1.14/BUG FIXES-20260311-163804.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: Prevent crash in the display of relevant attributes after provider upgrades +time: 2026-03-11T16:38:04.50368-04:00 +custom: + Issue: "38264" diff --git a/internal/command/jsonformat/structured/attribute_path/matcher.go b/internal/command/jsonformat/structured/attribute_path/matcher.go index d5d3c8cbbd..3fd74dfb95 100644 --- a/internal/command/jsonformat/structured/attribute_path/matcher.go +++ b/internal/command/jsonformat/structured/attribute_path/matcher.go @@ -18,7 +18,7 @@ import ( // // The caller of the above functions is required to know whether the next value // in the path is a list type or an object type and call the relevant function, -// otherwise these functions will crash/panic. +// otherwise no match will be returned. // // The Matches function returns true if the paths you have traversed until now // ends. @@ -151,8 +151,18 @@ func (p *PathMatcher) GetChildWithKey(key string) Matcher { continue } - if path[0].(string) == key { - child.Paths = append(child.Paths, path[1:]) + switch val := path[0].(type) { + case string: + if val == key { + child.Paths = append(child.Paths, path[1:]) + } + case float64: + // here we must assume the path being looked up no longer matches + // the given data structure, so the caller in incorrect. This is + // fine, because it only means that we don't match any paths. + default: + panic(fmt.Errorf("found invalid type within path (%v:%T), the validation shouldn't have allowed this to happen; this is a bug in Terraform, please report it", val, val)) + } } return child @@ -190,15 +200,12 @@ func (p *PathMatcher) GetChildWithIndex(index int) Matcher { switch val := path[0].(type) { case float64: - if int(path[0].(float64)) == index { + if int(val) == index { child.Paths = append(child.Paths, path[1:]) } case string: f, err := strconv.ParseFloat(val, 64) - if err != nil { - panic(fmt.Errorf("found invalid type within path (%v:%T), the validation shouldn't have allowed this to happen; this is a bug in Terraform, please report it", val, val)) - } - if int(f) == index { + if err == nil && int(f) == index { child.Paths = append(child.Paths, path[1:]) } default: diff --git a/internal/command/jsonformat/structured/attribute_path/matcher_test.go b/internal/command/jsonformat/structured/attribute_path/matcher_test.go index ce69b8715b..bf614fcf0c 100644 --- a/internal/command/jsonformat/structured/attribute_path/matcher_test.go +++ b/internal/command/jsonformat/structured/attribute_path/matcher_test.go @@ -254,3 +254,39 @@ func TestPathMatcher_MultiplePaths(t *testing.T) { t.Errorf("should not have partial matched at leaf level") } } + +// Since paths may be coming from relevant attributes, and those paths may no +// longer correspond to an updated schema, we can't always be certain the caller +// knows the correct type. +func TestPathMatcher_WrongKeyTypes(t *testing.T) { + var matcher Matcher + + matcher = &PathMatcher{ + Paths: [][]interface{}{ + { + float64(0), + "key", + float64(0), + }, + }, + } + + failed := matcher.GetChildWithKey("key") + if failed.Matches() || failed.MatchesPartial() { + t.Errorf("should not have any match at on failure") + } + + matcher = matcher.GetChildWithIndex(0).GetChildWithKey("key") + + if matcher.Matches() { + t.Errorf("should not have exact matched at first level") + } + if !matcher.MatchesPartial() { + t.Errorf("should have partial matched at first level") + } + + failed = matcher.GetChildWithKey("zero") + if failed.Matches() || failed.MatchesPartial() { + t.Errorf("should not have any match at on failure") + } +}