prometheus/node_exporter · error
failed to retrieve adjtimex stats
Error message
failed to retrieve adjtimex stats: %w
What it means
The timex collector calls unix.Adjtimex to read kernel time-sync status. If the syscall returns an error other than EPERM (which is downgraded to ErrNoData), it is wrapped as 'failed to retrieve adjtimex stats: %w'. Note the error may also surface through the ErrNoData path when permission is denied, so callers see either this error or a no-data skip.
Solutions
- Grant the process CAP_SYS_TIME (setcap cap_sys_time+ep) if EPERM-related behavior is desired, or run as root.
- Inspect the wrapped errno to determine the real cause (EFAULT, EINVAL, etc.).
- If seccomp/container policies block adjtimex, relax the policy or disable the timex collector.
- Accept ErrNoData semantics: permission-denied intentionally skips collection.
Defensive patterns
Strategy: try-catch
Validate before calling
// check effective capabilities before enabling timex collection // e.g. run as root or with cap_sys_time; capsh --print | grep cap_sys_time
Try / catch
if err := c.Update(ch); err != nil {
if errors.Is(err, ErrNoData) {
return // permission denied was downgraded; collection intentionally skipped
}
if strings.Contains(err.Error(), "failed to retrieve adjtimex stats") {
logger.Warn("timex unavailable", "err", err)
return
}
return err
} Prevention
- Run node_exporter with sufficient privileges (root or CAP_SYS_TIME).
- Check seccomp/container profiles don't block the adjtimex syscall.
- Distinguish ErrNoData (permission, intentional skip) from real errors.
- Test timex collection under the same user the service runs as.
When it happens
Trigger: timexCollector.Update when unix.Adjtimex(timex) fails with a non-EPERM errno (e.g. EFAULT from a bad buffer, or other kernel errors); with EPERM the collector logs a debug and returns ErrNoData instead.
Common situations: Running the exporter as a non-root user without the CAP_SYS_TIME capability in environments that restrict adjtimex (some hardened kernels/seccomp profiles block the syscall entirely, producing non-EPERM errors).
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Could not derive a human-readable chip type for
- interrupts empty
- ZFS / ZFS statistics are not available
- failed to open procfs
- could not get ARP entries
AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07).
Data as JSON: /api/errors/5e75dcf007266678.
Report an issue: GitHub.
Appendix: source
Thrown at collector/timex.go:174
"Is clock synchronized to a reliable server (1 = yes, 0 = no).",
nil, nil,
), prometheus.GaugeValue},
logger: logger,
}, nil
}
func (c *timexCollector) Update(ch chan<- prometheus.Metric) error {
var syncStatus float64
var divisor float64
var timex = new(unix.Timex)
status, err := unix.Adjtimex(timex)
if err != nil {
if errors.Is(err, os.ErrPermission) {
c.logger.Debug("Not collecting timex metrics", "err", err)
return ErrNoData
}
return fmt.Errorf("failed to retrieve adjtimex stats: %w", err)
}
if status == timeError {
syncStatus = 0
} else {
syncStatus = 1
}
if (timex.Status & staNano) != 0 {
divisor = nanoSeconds
} else {
divisor = microSeconds
}
ch <- c.syncStatus.mustNewConstMetric(syncStatus)
ch <- c.offset.mustNewConstMetric(float64(timex.Offset) / divisor)
ch <- c.freq.mustNewConstMetric(1 + float64(timex.Freq)/ppm16frac)
ch <- c.maxerror.mustNewConstMetric(float64(timex.Maxerror) / microSeconds)
ch <- c.esterror.mustNewConstMetric(float64(timex.Esterror) / microSeconds)View on GitHub (pinned to 17ddd77c59)