louislam/uptime-kuma · error · Error
Screenshot delay must be less than ${maxDelayFromInterval}ms
Error message
Screenshot delay must be less than ${maxDelayFromInterval}ms (0.5 × interval) What it means
Thrown by Monitor.validate() for type "real-browser" when screenshot_delay >= interval*1000*0.5. Even though it may fit under the page.goto bound, a delay of half the interval or more risks overlapping the next scheduled heartbeat and blocking the monitor loop.
Source
Thrown at server/model/monitor.js:1767
if (this.type === "real-browser") {
// screenshot_delay validation
if (this.screenshot_delay !== undefined && this.screenshot_delay !== null) {
const delay = Number(this.screenshot_delay);
if (isNaN(delay) || delay < 0) {
throw new Error("Screenshot delay must be a non-negative number");
}
// Must not exceed 0.8 * timeout (page.goto timeout is interval * 1000 * 0.8)
const maxDelayFromTimeout = this.interval * 1000 * 0.8;
if (delay >= maxDelayFromTimeout) {
throw new Error(`Screenshot delay must be less than ${maxDelayFromTimeout}ms (0.8 × interval)`);
}
// Must not exceed 0.5 * interval to prevent blocking next check
const maxDelayFromInterval = this.interval * 1000 * 0.5;
if (delay >= maxDelayFromInterval) {
throw new Error(`Screenshot delay must be less than ${maxDelayFromInterval}ms (0.5 × interval)`);
}
}
}
if (this.type === "mongodb" && this.databaseQuery) {
// Validate that databaseQuery is valid JSON
try {
JSON.parse(this.databaseQuery);
} catch (error) {
throw new Error(`Invalid JSON in database query: ${error.message}`);
}
}
}
/**
* Gets monitor notification of multiple monitor
* @param {Array} monitorIDs IDs of monitor to get
* @returns {Promise<LooseObject<any>>} objectView on GitHub (pinned to 6b5ea01557)
Solutions
- Set screenshot_delay to below 0.5 * interval * 1000 ms, e.g. for interval=60s keep delay < 30000ms.
- Prefer the smaller of the two bounds (0.5 * interval) as your working ceiling.
- Raise interval if a longer delay is genuinely needed.
Example fix
// before
{ type: "real-browser", interval: 60, screenshot_delay: 31000 }
// after
{ type: "real-browser", interval: 60, screenshot_delay: 25000 } Defensive patterns
Strategy: validation
Validate before calling
function delayFitsInterval(delayMs, intervalSec) {
return Number(delayMs) < intervalSec * 1000 * 0.5;
} Type guard
function isDelayUnderIntervalBound(delayMs, intervalSec) {
const n = Number(delayMs);
return Number.isFinite(n) && n < intervalSec * 1000 * 0.5;
} Try / catch
try {
await bean.validate();
} catch (e) {
if (/0\.5 . interval/.test(e.message)) return badRequest("screenshot_delay must be < 0.5 * interval ms");
throw e;
} Prevention
- Treat 0.5 * interval as the working ceiling, not 0.8.
- Re-validate the pair on any interval edit.
When it happens
Trigger: Save a real-browser monitor whose screenshot_delay passes the 0.8*interval check but is still >= 0.5*interval, e.g. interval=60 and screenshot_delay=31000 (>= 30000ms but < 48000ms). This is the stricter of the two screenshot guards.
Common situations: User tunes delay to just under the 0.8 bound and trips this tighter bound instead. Inheriting a delay value from a longer-interval template. Forgetting that the two screenshot guards are independent.
Related errors
- Screenshot delay must be less than ${maxDelayFromTimeout}ms
- Per-ping timeout must be between ${PING_PER_REQUEST_TIMEOUT_
- Timeout must be between ${PING_GLOBAL_TIMEOUT_MIN} and ${PIN
- Screenshot delay must be a non-negative number
- Service Name is required.
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/59d6f68c952d7215.
Report an issue: GitHub.