terraform icon indicating copy to clipboard operation
terraform copied to clipboard

Hint about failed conversion to list when returning error about mismatched tuple types in conditional

Open skeggse opened this issue 3 years ago • 2 comments

Terraform Version

Terraform v1.3.5
on darwin_amd64

Terraform Configuration Files

output "good_message" {
  value = flatten([
    for n in [{ o = [1] }, { o = [2] }] :
    n.o[0] == 1
    ? [n]
    : [merge({ a = 1 }, n)]
  ])
}

output "bad_message" {
  value = flatten([
    for n in [{ o = [1] }, { o = [2] }] :
    n.o[0] == 1
    ? [n]
    : [for k in [1, 3] : merge({ a = k }, n)]
  ])
}

Debug Output

not enabling TF_LOG because it's not necessary for this report.

$ tf validate
╷
│ Error: Inconsistent conditional result types
│
│   on main.tf line 26, in output "good_message":
│   25:     n.o[0] == 1
│   26:     ? [n]
│   27:     : [merge({ a = 1 }, n)]
│     ├────────────────
│     │ n.o[0] is 1
│
│ The true and false result expressions must have consistent types. Type mismatch for tuple element 0: The 'false' value includes object attribute "a", which is absent in the 'true' value.
╵
╷
│ Error: Inconsistent conditional result types
│
│   on main.tf line 26, in output "good_message":
│   25:     n.o[0] == 1
│   26:     ? [n]
│   27:     : [merge({ a = 1 }, n)]
│     ├────────────────
│     │ n.o[0] is 2
│
│ The true and false result expressions must have consistent types. Type mismatch for tuple element 0: The 'false' value includes object attribute "a", which is absent in the 'true' value.
╵
╷
│ Error: Inconsistent conditional result types
│
│   on main.tf line 35, in output "bad_message":
│   34:     n.o[0] == 1
│   35:     ? [n]
│   36:     : [for k in [1, 3] : merge({ a = k }, n)]
│     ├────────────────
│     │ n.o[0] is 1
│
│ The true and false result expressions must have consistent types. The 'true' tuple has length 1, but the 'false' tuple has length 2.
╵
╷
│ Error: Inconsistent conditional result types
│
│   on main.tf line 35, in output "bad_message":
│   34:     n.o[0] == 1
│   35:     ? [n]
│   36:     : [for k in [1, 3] : merge({ a = k }, n)]
│     ├────────────────
│     │ n.o[0] is 2
│
│ The true and false result expressions must have consistent types. The 'true' tuple has length 1, but the 'false' tuple has length 2.
╵

Expected Behavior

An error message for bad_message explaining that the problem was a missing field:

╷
│ Error: Inconsistent conditional result types
│
│   on main.tf line 35, in output "bad_message":
│   34:     n.o[0] == 1
│   35:     ? [n]
│   36:     : [for k in [1, 3] : merge({ a = k }, n)]
│     ├────────────────
│     │ n.o[0] is 1
│
│ The true and false result expressions must have consistent types. Type mismatch for tuple element 0: The 'false' value includes object attribute "a", which is absent in the 'true' value.
╵

Actual Behavior

The following error message, which, when embedded deep inside a for_each clause makes debugging pretty tricky.

╷
│ Error: Inconsistent conditional result types
│
│   on main.tf line 35, in output "bad_message":
│   34:     n.o[0] == 1
│   35:     ? [n]
│   36:     : [for k in [1, 3] : merge({ a = k }, n)]
│     ├────────────────
│     │ n.o[0] is 1
│
│ The true and false result expressions must have consistent types. The 'true' tuple has length 1, but the 'false' tuple has length 2.
╵

Steps to Reproduce

  1. terraform init
  2. terraform validate (or terraform plan)

Additional Context

No response

References

No response

skeggse avatar Nov 29 '22 00:11 skeggse

Thanks for this report!

crw avatar Dec 03 '22 01:12 crw

Hi @skeggse!

Looking at the expression you wrote in bad_message, it seems like this error message is correct because:

  • The two arms of the conditional have tuple types, but not the same tuple type.
  • The tuple element types are not compatible with one another, so Terraform cannot automatically coerce this into a list of objects.
  • Tuple types are not required to have homogeneous element types -- that's what differentiates tuple types from list types -- and so neither of the conditional arms are invalid when taken in isolation.

Because Terraform's attempts to guess what you might have meant and apply implicit type conversions failed, Terraform just fell back on returning the most direct description of the original problem: the conditional expression arms are of different tuple types.

While I can use my human intuition to infer from the context what you intended this expression to mean, I'm not sure how to express that as a rule we could write in code to produce the error message you proposed. It would require Terraform to guess that you intended these tuples to be converted to lists and that it's the failure to convert to list that was the problem, not the direct type inconsistency.

Being explicit that the conditional result arms are supposed to be lists gives Terraform the extra hint required to get the error message you wanted:

> flatten([
:     for n in [{ o = [1] }, { o = [2] }] :
:     n.o[0] == 1
:     ? tolist([n])
:     : tolist([for k in [1, 3] : merge({ a = k }, n)])
:   ])
╷
│ Error: Inconsistent conditional result types
│ 
│   on <console-input> line 4:
│   (source code not available)
│ 
│ The true and false result expressions must have consistent types. Mismatched list element types: The 'false' value includes object attribute "a",
│ which is absent in the 'true' value.
╵

...but without that hint Terraform concludes that you must've intended the results to be tuples, because the automatic conversion to list failed.

I agree that what happened here isn't ideal, but I'm not really sure what to do about it. The best idea I have is to extend the error message to explicitly acknowledge the failed attempt at type conversion somehow. For example:

The true and false result expressions must have consistent types. The 'true' tuple has length 1, but the 'false' tuple has length 2. The tuples have mismatched element types, so automatic conversion to list is also impossible.

We've run into trouble in the past putting this sort of extra content in error messages because folks who only skim error messages tend to shut off and fail to process the message at all when there's more than one possibility to consider. But perhaps it would be okay in this case, since I'd imagine that intent to convert to a list is the more likely case than trying to select between two different tuple types.

(This error message actually belongs to HCL rather than to Terraform, so if we choose to make a change like I described above then we'll need to do it in the HCL repository rather than here.)

apparentlymart avatar May 17 '24 23:05 apparentlymart