louislam/uptime-kuma · warning · Error
Root dispersion ${rootDispersion.toFixed(3)}ms exceeds thres
Error message
Root dispersion ${rootDispersion.toFixed(3)}ms exceeds threshold ${dispersionThreshold}ms What it means
Thrown when rootDispersion >= monitor.ntp_root_dispersion_threshold (default 500 ms). Root dispersion is the server's reported accumulated measurement error (read as a 32-bit fixed-point at packet offset 8 and converted to ms). High dispersion indicates the server itself is uncertain about its time, independent of the offset to this client.
Source
Thrown at server/monitor-types/ntp.js:51
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;
}
/**
* Query an NTP server via UDP
* @param {string} hostname NTP server hostname or IP
* @param {number} port NTP server port (usually 123)
* @param {number} timeout Timeout in milliseconds
* @returns {Promise<object>} Parsed NTP response data
*/
queryNTP(hostname, port, timeout) {
return new Promise((resolve, reject) => {
let client = null;
let settled = false;View on GitHub (pinned to 6b5ea01557)
Solutions
- Raise monitor.ntp_root_dispersion_threshold to a value the target normally stays under.
- Switch to a more stable, lower-stratum NTP source.
- Confirm the target daemon has had time to converge after restart.
Example fix
// before: monitor.ntp_root_dispersion_threshold = 500 // after: monitor.ntp_root_dispersion_threshold = 2000
Defensive patterns
Strategy: validation
Validate before calling
function validateDispersionThreshold(monitor) {
const t = Number(monitor.ntp_root_dispersion_threshold ?? 500);
if (!Number.isFinite(t) || t <= 0) throw new Error('ntp_root_dispersion_threshold must be > 0');
} Prevention
- Observe the target's typical root dispersion, then set the threshold above it with margin.
- Switch to a more stable NTP source if dispersion is persistently high.
- Let a restarted daemon converge before alerting.
When it happens
Trigger: Server returns a valid packet whose root-dispersion field, converted to ms, meets or exceeds dispersionThreshold. Happens with unstable server chains, recently restarted daemons whose filters haven't converged, or misbehaving appliance servers.
Common situations: Default 500 ms threshold too strict for a legitimate distant server; target's dispersion genuinely high due to poor upstream stability; software/appliance NTP reporting inflated dispersion.
Related errors
- Stratum ${stratum} meets or exceeds threshold ${stratumThres
- 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/544da1b0b20db5ce.
Report an issue: GitHub.