louislam/uptime-kuma · warning · Error

Stratum ${stratum} meets or exceeds threshold ${stratumThres

Error message

Stratum ${stratum} meets or exceeds threshold ${stratumThreshold}

What it means

Thrown when the server's stratum is at or above monitor.ntp_stratum_threshold (default 5). Stratum counts distance from the reference clock: 1 = directly attached, 2 = one hop, etc. A higher stratum means more accumulated error, so the monitor rejects servers that are too many hops away from a reference source.

Source

Thrown at server/monitor-types/ntp.js:41

        const port = monitor.port || 123;
        const timeout = (monitor.timeout || 10) * 1000;

        const ntpResult = await this.queryNTP(monitor.hostname, port, timeout);

        heartbeat.ping = dayjs().valueOf() - startTime;

        const { stratum, offset, rootDispersion, refid, roundTripDelay } = ntpResult;

        heartbeat.msg = `Stratum: ${stratum}, RefID: ${refid}, Offset: ${offset.toFixed(3)}ms, Delay: ${roundTripDelay.toFixed(3)}ms, Dispersion: ${rootDispersion.toFixed(3)}ms`;

        if (stratum === 16) {
            throw new Error("NTP server is unsynchronized (stratum 16)");
        }

        const stratumThreshold = monitor.ntp_stratum_threshold || 5;
        if (stratum >= stratumThreshold) {
            throw new Error(`Stratum ${stratum} meets or exceeds threshold ${stratumThreshold}`);
        }

        const offsetThreshold = monitor.ntp_time_offset_threshold || 1000;
        if (Math.abs(offset) >= offsetThreshold) {
            throw new Error(`Time offset ${offset.toFixed(3)}ms exceeds threshold ${offsetThreshold}ms`);
        }

        const dispersionThreshold = monitor.ntp_root_dispersion_threshold || 500;
        if (rootDispersion >= dispersionThreshold) {
            throw new Error(
                `Root dispersion ${rootDispersion.toFixed(3)}ms exceeds threshold ${dispersionThreshold}ms`
            );
        }

        heartbeat.status = UP;
    }

    /**

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Raise monitor.ntp_stratum_threshold to a value the legitimate target stays under (e.g. 8 or 10).
  2. Point the monitor at a closer NTP source (lower stratum) — a local stratum-2 instead of a distant stratum-6.
  3. Confirm the target's own upstreams are healthy so its advertised stratum stays low.

Example fix

// before: monitor.ntp_stratum_threshold = 5  (target is stratum 6)
// after:  monitor.ntp_stratum_threshold = 8
Defensive patterns

Strategy: validation

Validate before calling

function validateStratumThreshold(monitor) {
  const t = Number(monitor.ntp_stratum_threshold ?? 5);
  if (!Number.isFinite(t) || t < 2 || t > 15) throw new Error('ntp_stratum_threshold must be 2..15');
}

Prevention

When it happens

Trigger: Server returns a valid packet with stratum in [0..15] (not 16) and stratum >= stratumThreshold. E.g. querying a stratum-3 server with the default threshold of 5 will NOT fire, but raising a chain of stratum-6 servers will.

Common situations: Threshold left at default 5 while monitoring a deep-stratum internal server; monitoring a public server whose stratum drifted up; policy requires a tighter stratum than the default.

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/c4a11239ec0a6d8e. Report an issue: GitHub.