Summary
When an optional numeric or string field is set to its zero value, the provider sends it, the SigNoz API normalises it to null, and the plugin framework then rejects the provider's own round-trip. The apply fails and Terraform leaves the resource tainted — even though the API call succeeded and the rule was created correctly.
Two fields in signoz_rule, same behaviour:
Error: Provider produced inconsistent result after apply
When applying changes to signoz_rule.this, provider
"provider[\"registry.terraform.io/signoz/signoz\"]" produced an unexpected
new value: .condition.absent_for: was cty.NumberIntVal(0), but now null.
Error: Provider produced inconsistent result after apply
When applying changes to signoz_rule.this, provider
"provider[\"registry.terraform.io/signoz/signoz\"]" produced an unexpected
new value: .condition.composite_query.unit: was cty.StringVal(""), but now null.
This class has been closed per-field at least twice already
I checked for duplicates before filing, and this looks systemic rather than field-specific:
| issue |
field |
closed |
| #147 |
condition…metrics.step_interval — "0" → null |
2026-08-02, completed |
| #80 |
signoz_dashboard.variables — "{}" → "" |
2026-07-22, completed |
#147 is the same defect as this one, one field over. Two observations offered in case they are useful:
So on the newest published version the class is still live, with nothing pending that would resolve it. Rather than file "here is a third field", the more useful ask is a single fix for the zero-value round-trip, so the next optional field does not reproduce it.
Why this is worse than a failed apply
The resource is left tainted, so the next terraform apply plans to destroy and recreate a live alerting rule. For a rule wired to a notification channel that is a silent gap in alert coverage plus a new rule id. Recovery without recreating requires an explicit terraform untaint, which is easy to miss because the message says "this is a bug in the provider" rather than "your resource is now tainted".
Reproduction
absent_for = 0 and unit = "" each trigger it independently:
resource "signoz_rule" "repro" {
alert = "zero value repro"
alert_type = "METRIC_BASED_ALERT"
rule_type = "threshold_rule"
schema_version = "v2alpha1"
version = "v5"
disabled = false
condition = {
absent_for = 0 # <-- API returns null; apply fails
selected_query_name = "A"
composite_query = {
unit = "" # <-- same defect, independently reproducible
panel_type = "graph"
query_type = "builder"
queries = [{
type = "builder_query"
spec = {
metrics = {
name = "A"
signal = "metrics"
aggregations = [{
metric_name = "<any existing metric>"
time_aggregation = "latest"
space_aggregation = "max"
}]
filter = { expression = "" }
}
}
}]
}
thresholds = {
basic = {
kind = "basic"
spec = [{
name = "critical"
target = 1
op = "above"
match_type = "at_least_once"
}]
}
}
}
evaluation = {
rolling = { kind = "rolling", spec = { eval_window = "30m0s", frequency = "5m" } }
}
}
Setting absent_for to any non-zero value (e.g. 45) round-trips correctly and converges — so it is specific to the zero value, not to the field.
Verified server-side
Reading the rule back after the failed apply confirms it was created, and that the API stores null rather than the zero value:
GET /api/v1/rules/<id> -> "absentFor": null (sent 0)
GET /api/v1/rules/<id> -> no "unit" key (sent "")
Expected
Any of:
- Treat a zero value as equivalent to
null when reading back, so the round-trip is consistent — this matches what the API already does;
- Preserve the zero value server-side; or
- Give these attributes semantic-equality /
UseStateForUnknown handling so an intentional 0 or "" does not fail.
Current workaround
Omit the field entirely rather than setting its zero value. Both affected resources then plan No changes. This only works because zero happens to be semantically equivalent to absent in both cases — it would not be if a zero were meaningful.
Environment
|
|
| provider |
SigNoz/signoz 0.1.1 (latest release) |
| Terraform |
v1.8.5 |
| SigNoz |
v0.135.1, EE |
Summary
When an optional numeric or string field is set to its zero value, the provider sends it, the SigNoz API normalises it to
null, and the plugin framework then rejects the provider's own round-trip. The apply fails and Terraform leaves the resource tainted — even though the API call succeeded and the rule was created correctly.Two fields in
signoz_rule, same behaviour:This class has been closed per-field at least twice already
I checked for duplicates before filing, and this looks systemic rather than field-specific:
condition…metrics.step_interval—"0"→nullsignoz_dashboard.variables—"{}"→""#147 is the same defect as this one, one field over. Two observations offered in case they are useful:
closedwithcommit_id: nulland no linked PR, so I could not find the change that fixed it.mainis only 3 commits ahead — test(integration): cover OpenTofu alongside Terraform #145 (tests), chore(tests): apply the py-comments rule #146 (test comments), chore(codegen): sync generated code to upstream openapi spec #151 (codegen sync, whose description says "No other drift"). None appears to touch this.So on the newest published version the class is still live, with nothing pending that would resolve it. Rather than file "here is a third field", the more useful ask is a single fix for the zero-value round-trip, so the next optional field does not reproduce it.
Why this is worse than a failed apply
The resource is left tainted, so the next
terraform applyplans to destroy and recreate a live alerting rule. For a rule wired to a notification channel that is a silent gap in alert coverage plus a new rule id. Recovery without recreating requires an explicitterraform untaint, which is easy to miss because the message says "this is a bug in the provider" rather than "your resource is now tainted".Reproduction
absent_for = 0andunit = ""each trigger it independently:Setting
absent_forto any non-zero value (e.g.45) round-trips correctly and converges — so it is specific to the zero value, not to the field.Verified server-side
Reading the rule back after the failed apply confirms it was created, and that the API stores
nullrather than the zero value:Expected
Any of:
nullwhen reading back, so the round-trip is consistent — this matches what the API already does;UseStateForUnknownhandling so an intentional0or""does not fail.Current workaround
Omit the field entirely rather than setting its zero value. Both affected resources then plan
No changes. This only works because zero happens to be semantically equivalent to absent in both cases — it would not be if a zero were meaningful.Environment
SigNoz/signoz0.1.1 (latest release)