#1779·gatus

DNS check: [DNS_ANSWER] is not a placeholder and undefined placeholders aren't caught at config validation

Author: jrimmerCreated Aug 24, 2026Updated Sep 8, 2026

Description

The type: dns check exposes only [DNS_RCODE] as a condition placeholder, so there is no way to assert that a DNS query actually returned records (as opposed to NOERROR with zero answers / NODATA).

Worse, referencing an undefined placeholder (e.g. [DNS_ANSWER].length > 0) is silently accepted at config-validation time and then silently evaluates to false (measured value 0) on every run — no error, no warning. This is very hard to debug because the config looks valid and [DNS_RCODE] == NOERROR in the same conditions list succeeds, so the endpoint is red for a reason that isn't obvious.

Steps to Reproduce

  1. Configure a DNS check against a resolver that returns real A records:
    yaml
    endpoints:
      - name: dns-forward
        url: "10.1.0.2"
        interval: 1m
        dns:
          query-name: google.com
          query-type: A
        conditions:
          - "[DNS_RCODE] == NOERROR"
          - "[DNS_ANSWER].length > 0"
  2. Start Gatus. Config validation passes — the endpoint is counted (Validated N endpoints) with no panic and no invalid condition error.
  3. Observe the endpoint result. [DNS_RCODE] == NOERRORsuccess=true. [DNS_ANSWER].length > 0success=false (measured value 0) on every check, even though the query returns real A records.

Expected Behavior

Either:

  • A placeholder that exposes the DNS answer and/or answer count, so a user can write has([DNS_ANSWER]), len([DNS_ANSWER]) > 0, etc.; or
  • At minimum, config validation should fail fast with a clear invalid condition error when a condition references an undefined placeholder, instead of silently producing a condition that always fails (or, worse, spuriously passes for forms like [DNS_ANSWER] != '').

Actual Behavior

[DNS_ANSWER] is not a recognized placeholder — config/endpoint/placeholder.go defines only DNSRCodePlaceholder = "[DNS_RCODE]". ResolvePlaceholder falls through to return originalPlaceholder, nil for unknown placeholders (to support literal string comparisons), so the condition's left-hand side becomes the literal string [DNS_ANSWER], which numeric comparisons treat as 00 > 0 is false. No validation error is raised at startup.

Environment

  • Gatus version: v5.36.0 (built from source)
  • OS: Linux
  • Resolver under test: Technitium DNS (though any resolver returning answers reproduces it)

Additional Context

  • The resolver does return answers: dig +short @10.1.0.2 google.com A → IPv4 addresses.
  • Source reference: config/endpoint/placeholder.go — only [DNS_RCODE] is defined for DNS checks; there is no [DNS_ANSWER], [DNS_RESULT], or answer-count placeholder. Unknown placeholders are returned verbatim (line ~189: "Return the original placeholder if we can't resolve it... allows for literal string comparisons"), which silently breaks numeric/len/has conditions.