{"record":{"id":"5e75dcf007266678","repo":"prometheus/node_exporter","slug":"failed-to-retrieve-adjtimex-stats-w","errorCode":null,"errorMessage":"failed to retrieve adjtimex stats: %w","messagePattern":"failed to retrieve adjtimex stats: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"collector/timex.go","lineNumber":174,"sourceCode":"\t\t\t\"Is clock synchronized to a reliable server (1 = yes, 0 = no).\",\n\t\t\tnil, nil,\n\t\t), prometheus.GaugeValue},\n\t\tlogger: logger,\n\t}, nil\n}\n\nfunc (c *timexCollector) Update(ch chan<- prometheus.Metric) error {\n\tvar syncStatus float64\n\tvar divisor float64\n\tvar timex = new(unix.Timex)\n\n\tstatus, err := unix.Adjtimex(timex)\n\tif err != nil {\n\t\tif errors.Is(err, os.ErrPermission) {\n\t\t\tc.logger.Debug(\"Not collecting timex metrics\", \"err\", err)\n\t\t\treturn ErrNoData\n\t\t}\n\t\treturn fmt.Errorf(\"failed to retrieve adjtimex stats: %w\", err)\n\t}\n\n\tif status == timeError {\n\t\tsyncStatus = 0\n\t} else {\n\t\tsyncStatus = 1\n\t}\n\tif (timex.Status & staNano) != 0 {\n\t\tdivisor = nanoSeconds\n\t} else {\n\t\tdivisor = microSeconds\n\t}\n\n\tch <- c.syncStatus.mustNewConstMetric(syncStatus)\n\tch <- c.offset.mustNewConstMetric(float64(timex.Offset) / divisor)\n\tch <- c.freq.mustNewConstMetric(1 + float64(timex.Freq)/ppm16frac)\n\tch <- c.maxerror.mustNewConstMetric(float64(timex.Maxerror) / microSeconds)\n\tch <- c.esterror.mustNewConstMetric(float64(timex.Esterror) / microSeconds)","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/prometheus/node_exporter/blob/17ddd77c59ba27e1508e9f7894b1e55b44d6aed3/collector/timex.go#L156-L192","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// check effective capabilities before enabling timex collection\n// e.g. run as root or with cap_sys_time; capsh --print | grep cap_sys_time","typeGuard":null,"tryCatchPattern":"if err := c.Update(ch); err != nil {\n    if errors.Is(err, ErrNoData) {\n        return // permission denied was downgraded; collection intentionally skipped\n    }\n    if strings.Contains(err.Error(), \"failed to retrieve adjtimex stats\") {\n        logger.Warn(\"timex unavailable\", \"err\", err)\n        return\n    }\n    return err\n}","preventionTips":["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."],"tags":["linux","timex","syscall","ntp"],"backgroundTag":"permission-denied","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"}