{"record":{"id":"acf082604adc725f","repo":"prometheus/node_exporter","slug":"couldn-t-get-sntp-reply-w","errorCode":null,"errorMessage":"couldn't get SNTP reply: %w","messagePattern":"couldn't get SNTP reply: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"collector/ntp.go","lineNumber":135,"sourceCode":"\t\t), prometheus.GaugeValue},\n\t\tsanity: typedDesc{prometheus.NewDesc(\n\t\t\tprometheus.BuildFQName(namespace, ntpSubsystem, \"sanity\"),\n\t\t\t\"NTPD sanity according to RFC5905 heuristics and configured limits.\",\n\t\t\tnil, nil,\n\t\t), prometheus.GaugeValue},\n\t\tlogger: logger,\n\t}, nil\n}\n\nfunc (c *ntpCollector) Update(ch chan<- prometheus.Metric) error {\n\tresp, err := ntp.QueryWithOptions(*ntpServer, ntp.QueryOptions{\n\t\tVersion: *ntpProtocolVersion,\n\t\tTTL:     *ntpIPTTL,\n\t\tTimeout: time.Second, // default `ntpdate` timeout\n\t\tPort:    *ntpServerPort,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"couldn't get SNTP reply: %w\", err)\n\t}\n\n\tch <- c.stratum.mustNewConstMetric(float64(resp.Stratum))\n\tch <- c.leap.mustNewConstMetric(float64(resp.Leap))\n\tch <- c.rtt.mustNewConstMetric(resp.RTT.Seconds())\n\tch <- c.offset.mustNewConstMetric(resp.ClockOffset.Seconds())\n\tif resp.ReferenceTime.Unix() > 0 {\n\t\t// Go Zero is   0001-01-01 00:00:00 UTC\n\t\t// NTP Zero is  1900-01-01 00:00:00 UTC\n\t\t// UNIX Zero is 1970-01-01 00:00:00 UTC\n\t\t// so let's keep ALL ancient `reftime` values as zero\n\t\tch <- c.reftime.mustNewConstMetric(float64(resp.ReferenceTime.UnixNano()) / 1e9)\n\t} else {\n\t\tch <- c.reftime.mustNewConstMetric(0)\n\t}\n\tch <- c.rootDelay.mustNewConstMetric(resp.RootDelay.Seconds())\n\tch <- c.rootDispersion.mustNewConstMetric(resp.RootDispersion.Seconds())\n","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/prometheus/node_exporter/blob/17ddd77c59ba27e1508e9f7894b1e55b44d6aed3/collector/ntp.go#L117-L153","documentation":"The ntp collector's Update performs an SNTP query against the configured local NTP server (with the configured protocol version, IP TTL, and 1-second timeout). Any failure — network unreachable, TTL exceeded, timeout, or malformed reply — is wrapped in this message. Because Update errors surface as scrape failures, an unreachable server makes every scrape of this collector fail.","triggerScenarios":"cgetQuery/ntp query returns an error during Update: the configured loopback/local NTP server is not running (UDP port closed), a firewall drops the reply, IP_TTL=1 is too low for a non-directly-connected server, or the 1-second timeout expires.","commonSituations":"Enabling --collector.ntp on hosts without a local ntpd/chronyd listening; pointing at a loopback IP where nothing listens on port 123 (connection refused/ICMP port unreachable); placing the exporter more than one hop from the server so TTL=1 probes never arrive; transient network loss during a scrape.","solutions":["Verify a local NTP server is listening: 'chronyc statistics' / 'ss -ulpn | grep 123' and 'ntpdate -q 127.0.0.1'.","Ensure the configured server is directly reachable at IP TTL=1 (same subnet/loopback); otherwise fix topology or (if you own the server) reconsider placement.","Check firewall rules allow outbound UDP 123 and inbound replies.","Point --collector.ntp.server at the correct local server IP.","If NTP checks are not needed, disable with --collector.ntp or --disable-collector.ntp to stop scrape errors."],"exampleFix":"// before: no local NTP server on 127.0.0.1\n//   --collector.ntp.server=127.0.0.1  -> scrape error every interval\n// after: run chrony/ntpd locally or disable the collector\n//   chronyd  (allow 127.0.0.1)\n// or\n//   --disable-collector.ntp","handlingStrategy":"retry","validationCode":"// shell: verify the local NTP server answers before enabling the collector\nchronyc -a 'tracker' >/dev/null 2>&1 || ntpq -c rv 127.0.0.1 >/dev/null 2>&1 || echo \"no local NTP server on $NTP_SERVER\"","typeGuard":null,"tryCatchPattern":"// handle transient query failures without failing every scrape\nif err := query(&resp); err != nil {\n    if isTimeout(err) || isRefused(err) {\n        c.logger.Warn(\"ntp query failed, will retry next scrape\", \"err\", err)\n        return ErrNoData // degrade gracefully instead of erroring\n    }\n    return fmt.Errorf(\"couldn't get SNTP reply: %w\", err)\n}","preventionTips":["Run a local ntpd/chronyd before enabling --collector.ntp.","Ensure the server is within one IP hop (TTL=1 probe) of the exporter.","Allow UDP 123 both directions in host/firewall rules.","Accept that the 1s timeout makes transient loss visible; alert on sustained failures only.","Disable the collector on hosts where NTP checks are out of scope."],"tags":["ntp","sntp","network","udp","timeout"],"backgroundTag":"network-request-failed","analyzedSha":"17ddd77c59ba27e1508e9f7894b1e55b44d6aed3","analyzedAt":"2026-09-07T17:54:06.211Z","contentChangedAt":"2026-09-07T17:54:06.211Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}