alerting: don't print a bare "value:" when ValueString is empty #96

Merged
admin_emi merged 1 commit from fix/alert-template-empty-value into dev 2026-07-25 18:17:39 +00:00
Owner

Follow-up to #94. A resolved notice landed in #ops-alerts reading value: with nothing after it.

Cause

The Discord contact point prints the value line unconditionally:

`value: {{ .ValueString }}`

.ValueString is only populated when the notification came from an evaluation that produced refIds. An instance resolved by Grafana's staleness handling has none — no evaluation ever returns that label set again, so the state manager expires it rather than evaluating it to Normal.

That path is triggered by a rule title change, because the title becomes the alertname label and the old label set is orphaned. #94 renamed ProdWorkerContainerSilentProdWorkerHeartbeatMissing, which orphaned the instance that had been firing since 2026-07-24T21:05Z; it expired at 18:05:47Z and posted its resolved notice with an empty ValueString.

So the empty field is a real (if narrow) template bug, not a one-off cosmetic artifact of that rename — any future rule rename or label change hits it again.

Fix

Guard the whole line, not the substitution:

{{ if .ValueString }}`value: {{ .ValueString }}`
{{ end }}

An unguarded {{ .ValueString }} is harmless by itself — printing the label with nothing behind it is the bug. An empty labelled field reads as a value that failed to compute; an absent one correctly reads as "not applicable here".

Verification

The file carries an explicit warning that a template error here breaks every notification silently, so this was not eyeballed. Both the title and message templates were extracted from the YAML and executed through Go's text/template with ValueString set and empty:

  • populated → output byte-identical to the current format, `value: [ var='A' ... ]` intact
  • empty → the line is omitted entirely, no stray blank
  • both parse and execute with no error

Nothing else in the message changes.

Follow-up to #94. A resolved notice landed in #ops-alerts reading `value:` with nothing after it. ## Cause The Discord contact point prints the value line unconditionally: ``` `value: {{ .ValueString }}` ``` `.ValueString` is only populated when the notification came from an evaluation that produced refIds. An instance resolved by Grafana's **staleness handling** has none — no evaluation ever returns that label set again, so the state manager expires it rather than evaluating it to `Normal`. That path is triggered by a rule `title` change, because the title becomes the `alertname` label and the old label set is orphaned. #94 renamed `ProdWorkerContainerSilent` → `ProdWorkerHeartbeatMissing`, which orphaned the instance that had been firing since 2026-07-24T21:05Z; it expired at 18:05:47Z and posted its resolved notice with an empty ValueString. So the empty field is a real (if narrow) template bug, not a one-off cosmetic artifact of that rename — any future rule rename or label change hits it again. ## Fix Guard the whole line, not the substitution: ``` {{ if .ValueString }}`value: {{ .ValueString }}` {{ end }} ``` An unguarded `{{ .ValueString }}` is harmless by itself — printing the *label* with nothing behind it is the bug. An empty labelled field reads as a value that failed to compute; an absent one correctly reads as "not applicable here". ## Verification The file carries an explicit warning that a template error here breaks **every** notification silently, so this was not eyeballed. Both the `title` and `message` templates were extracted from the YAML and executed through Go's `text/template` with `ValueString` set and empty: - **populated** → output byte-identical to the current format, `` `value: [ var='A' ... ]` `` intact - **empty** → the line is omitted entirely, no stray blank - both parse and execute with no error Nothing else in the message changes.
admin_emi added 1 commit 2026-07-25 18:16:01 +00:00
alerting: don't print a bare "value:" when ValueString is empty
All checks were successful
secrets-guard / encrypted (pull_request) Successful in 5s
PR build (required check) / changes (pull_request) Successful in 11s
shell-lint / shellcheck (pull_request) Successful in 7s
PR build (required check) / build-backend (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Successful in 23s
PR build (required check) / gate (pull_request) Successful in 2s
9d5e87b594
The Discord template printed `value: {{ .ValueString }}` unconditionally, so a
notification carrying no value rendered as "value:" with nothing after it —
which reads as a value that failed to compute rather than one that was never
applicable.

ValueString is only populated when the notification came from an evaluation
that produced refIds. An instance resolved by Grafana's staleness handling has
none: no evaluation returns it again, so the state manager expires it. That is
triggered by a rule title change, since the title becomes the alertname label
and the old label set is orphaned — renaming ProdWorkerContainerSilent to
ProdWorkerHeartbeatMissing did it and posted exactly that message.

Guarding the whole line rather than the substitution: an unguarded
{{ .ValueString }} is harmless on its own, printing the label is the bug.

Verified by executing both templates through text/template with ValueString
set and empty: parses and executes clean, populated output is byte-identical
to the previous format, empty case omits the line.
admin_emi merged commit b32b5841f3 into dev 2026-07-25 18:17:39 +00:00
admin_emi referenced this pull request from a commit 2026-08-01 14:29:41 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Jinemi/thermograph#96
No description provided.