Skip to content

[bug] signoz_rule: zero-valued fields (absent_for = 0, unit = "") fail their own round-trip and taint the resource #153

Description

@ipryseski

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:

  1. Treat a zero value as equivalent to null when reading back, so the round-trip is consistent — this matches what the API already does;
  2. Preserve the zero value server-side; or
  3. 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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingresource/ruleIssues related to signoz_rule resource or data source

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions