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
- Raise monitor.ntp_stratum_threshold to a value the legitimate target stays under (e.g. 8 or 10).
- Point the monitor at a closer NTP source (lower stratum) — a local stratum-2 instead of a distant stratum-6.
- 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
- Set ntp_stratum_threshold above the target's normal stratum (observe with ntpq -p).
- Remember stratum 16 is a hard failure handled separately and cannot be allowed by this threshold.
- Use a closer NTP source if the target persistently reports a deep stratum.
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
- Root dispersion ${rootDispersion.toFixed(3)}ms exceeds thres
- Hostname is required
- Time offset ${offset.toFixed(3)}ms exceeds threshold ${offse
- NTP server is unsynchronized (stratum 16)
- Invalid NTP response: expected 48+ bytes, got ${msg.length}
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/c4a11239ec0a6d8e.
Report an issue: GitHub.