Summary
A parameter whose default is an unknown value can surface either as an error diagnostic or as a silent null, depending on how the unknown arrives. Both cases are "the value could not be resolved", but only one of them tells the user.
Repro
testdata/resourceclosure2 on main (from #224):
resource "docker_image" "base" { name = "large" }
data "coder_parameter" "computed" {
name = "computed"
type = "string"
default = docker_image.base.image_id # computed attribute, unknown at plan time
}
Result on main:
value=cty.NullVal(cty.String) valid=false known=false null=true default=cty.DynamicVal diags=0
DefaultValue is cty.DynamicVal, Value ends up as a typed null, and the parameter carries no diagnostics.
Compare a default that is unknown because a reference has no value (for example default = local.x where local.x reads a block that does not exist). There the value stays an unknown and ParameterUsageDiagnostics emits:
Parameter value is unknown, it likely includes a reference without a value
Where it diverges
extract/parameter.go, ParameterUsageDiagnostics:
if p.Value.Value.IsNull() {
// Allow null values
} else if !p.Value.Valid() {
...
} else if !p.Value.IsKnown() {
... "Parameter value is unknown" ...
}
The null branch is checked first, so an unknown default that is coerced to a typed null on the way to Value skips both the "not valid" and "unknown" branches, even though Valid() and IsKnown() are both false on it.
Expected
Either both shapes report the same "unknown / unresolved" diagnostic, or neither does. A user writing default = <computed attribute> gets a required-but-empty parameter in the form with no explanation today.
Notes
Filed by Emyrk with Coder Agents assistance.
Summary
A parameter whose
defaultis an unknown value can surface either as an error diagnostic or as a silent null, depending on how the unknown arrives. Both cases are "the value could not be resolved", but only one of them tells the user.Repro
testdata/resourceclosure2onmain(from #224):Result on
main:DefaultValueiscty.DynamicVal,Valueends up as a typed null, and the parameter carries no diagnostics.Compare a default that is unknown because a reference has no value (for example
default = local.xwherelocal.xreads a block that does not exist). There the value stays an unknown andParameterUsageDiagnosticsemits:Where it diverges
extract/parameter.go,ParameterUsageDiagnostics:The null branch is checked first, so an unknown default that is coerced to a typed null on the way to
Valueskips both the "not valid" and "unknown" branches, even thoughValid()andIsKnown()are both false on it.Expected
Either both shapes report the same "unknown / unresolved" diagnostic, or neither does. A user writing
default = <computed attribute>gets a required-but-empty parameter in the form with no explanation today.Notes
computedvector there pins the current behavior withap().unknown()and no diagnostic assertion, so it will not block a fix.mainwith no pruning.Filed by Emyrk with Coder Agents assistance.