New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Terraform 0.14 crashes, Terraform 1.0 reports inconsistent data type only when array is in conditional statement #30866
Comments
I believe this is the same bug I just saw with Terraform 1.1.7 on Linux x86-64. Actual encounter was with locals {
value = concat(
[{ x = 0 }],
var.some_input_var == null ? [] : [
{
key1 = [
[
{ x = 1 }
]
]
},
{
key2 = ["y"]
},
])
} This gives:
Replacing the true branch with
Removing the conditional and keeping either branch makes the problem vanish. |
Hi @AndreiPotemkin, Aside from this error message being confusing, this does seem like correct behavior to me. Both the true and false arms of a conditional expression must have the same type so that Terraform can determine the result type even if the condition isn't known yet, and a tuple with two elements is a different type than a tuple with no elements. One possible answer is to convert them both to be lists, because (unlike for tuple types) the numby of elements is part of the value of a list, not part of the type: a list of two strings is the same type as a list of zero strings. That would not work for @laszlocsomor's example though, because in that case there really is no single type that both the true and false expressions could be converted to: the false arm itself can't be a list because the two elements of the tuple are of different types. That problem will require a different solution that doesn't use a conditional expression, though I'm not sure what to suggest since the example seems to be contrived rather than a real configuration. |
(regarding the confusing error message: Terraform really wants to say that the two tuple types have different numbers/types of elements, but it's only reporting the top level type "tuple" and not describing the elements inside.) |
@apparentlymart, unless I misunderstand your response I would disagree with your point about how handling of arms of conditional expression plays into this problem.
However if attribute arr is defined as a list in one variable but as null in the other one it causes the problem only if list [local.a, local.b] is constructed in one of the arms of conditional statement. Maybe you can suggest an alternative solution to the actual problem I am trying to solve which can be described as follows:
Problem arises from scenario where some rules may require a single port to be supplied and others require a list of ports meaning that some objects being added into a list have ports attribute set to a list and other have same attribute set to null which my simplified code simulates. |
Thanks for the explanation @apparentlymart. This makes sense.
That's OK, thanks anyway! It was an actual example -- a JSON string passed to |
Thanks for clarifying your concern, @laszlocsomor! The underlying concern here was obscured a bit by you only sharing a part of the error message, making me think you were reporting that the error message was incorrect rather than that the behavior was incorrect. However, I can see one specific misbehavior here which I think is the root cause of the problem. Trying some expressions in
The first two of these are correct:
I can see the likely cause of this unexpected result in the generic definition that we share across all of these terraform/internal/lang/funcs/conversion.go Lines 24 to 36 in 3a72639
The above states that the argument can be of any type ( I think that just adding Doing the above would not make the original configuration as presented immediately work, because the given values are ambiguous enough about the intended result type that the automatic type inference algorithm prefers to give up and return an error rather than make an non-confident guess what the author intended. However, I believe fixing the above so that locals {
v = true
a = {
val = 1, arr = tolist(null)
}
b = {
val = tonumber(null), arr = tolist([1])
}
c = concat(
[local.a,local.b],
local.v ? [local.a,local.b] : []
)
} The |
@apparentlymart, I tried few more scenarios in Terraform console and it does appear [] construct is handled differently in conditional statement.
works however
fails Further analysis shows this interesting behavior
evaluates b in both elements to [] however when used in the conditional the same construct evaluates to tolist conversion
And finally when b array in both objects has same number of elements it does not produce tolist conversion
I sure hope PR #30879 properly handles presented failing case |
Hi @AndreiPotemkin, Yes, the conditional operator does make some attempts to automatically fix inconsistencies between the types of the true and false results through automatic type inference and conversion. This typically works as long as at least one expression is precisely typed enough that Terraform can see how to convert others which contain unknown-typed values like For example, if given a tuple where all elements are strings and a tuple with no elements, Terraform will assume that you intended both to be off type However, Terraform will give up and return an error if there isn't a clear path to automatic conversion to match both results. In that case the best approach is to add more type information using the type conversion functions. The associated pull request fixes an issue where the type conversion functions don't add type information to null values passed directly, and so after that is merged it will be possible to use the type conversion functions to explain what type you intended a null value to have when Terraform either cannot guess or it guesses incorrectly. |
#30879 addresses this issue by fixing a bug in the type conversion functions which was previously making it impossible to give Terraform the type information necessary to resolve type inference for the ambiguous input shown in the original report. The original input still returns the same error, which is a correct error because the untyped nulls in the
However, with that bug fixed we can use locals {
v = true
a = {
val = 1, arr = tolist(null)
}
b = {
val = tonumber(null), arr = tolist([1])
}
c = concat(
[local.a,local.b],
local.v ? [local.a,local.b]: []
)
}
output "result" {
value = local.c
}
In the final output from
Since the overall result of We also have another change pending over in the HCL repository to improve the error message about the conditional result types being inconsistent. We plan to adopt that into Terraform once it's merged, but we'll keep track of that separately since it's only an error message improvement and so can be updated at any time (including in a patch release) without changing the behavior of anything in the language. |
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. |
I think there might be an issue with the way Terraform handles list elements when in conditional statement compared to unconditional logic.
Actual problem has been encountered with Azure network security rule definitions but simplified code reproduces the problem.
Consider 2 local variables:
if these 2 values are placed in an array unconditionally
it works fine but if same array is built conditionally
it crashes Terraform 0.14 and in later versions reports error
│ │ local.a is object with 2 attributes
│ │ local.b is object with 2 attributes
To reproduce the issue attempt to execute 'terraform plan' on following:
When v = false plan succeeds, when v = true plan fails
Possible solution to change definition of a to
would pass Terraform check but will fail on apply as Azure network security rule only supports one of the 2 values to be provided, for example source_port_range and source_port_ranges (https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_rule#source_port_range)
The text was updated successfully, but these errors were encountered: