louislam/uptime-kuma · error · Error
Screenshot delay must be less than ${maxDelayFromTimeout}ms
Error message
Screenshot delay must be less than ${maxDelayFromTimeout}ms (0.8 × interval) What it means
Thrown by Monitor.validate() for type "real-browser" when screenshot_delay >= interval*1000*0.8. The 0.8 factor mirrors the page.goto navigation timeout (0.8 * interval), so a delay at or above it would fire after the navigation has already timed out, making the screenshot useless.
Source
Thrown at server/model/monitor.js:1761
);
}
this.timeout = pingGlobalTimeout;
}
}
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}`);
}
}View on GitHub (pinned to 6b5ea01557)
Solutions
- Lower screenshot_delay to below 0.8 * interval * 1000 ms, e.g. for interval=20s keep delay < 16000ms.
- Alternatively raise the monitor interval so the same delay fits under the 0.8 threshold.
- Recompute the bound whenever you change interval.
Example fix
// before
{ type: "real-browser", interval: 20, screenshot_delay: 17000 }
// after
{ type: "real-browser", interval: 20, screenshot_delay: 12000 } Defensive patterns
Strategy: validation
Validate before calling
function delayFitsTimeout(delayMs, intervalSec) {
return Number(delayMs) < intervalSec * 1000 * 0.8;
} Type guard
function isDelayUnderTimeoutBound(delayMs, intervalSec) {
const n = Number(delayMs);
return Number.isFinite(n) && n < intervalSec * 1000 * 0.8;
} Try / catch
try {
await bean.validate();
} catch (e) {
if (/0\.8 . interval/.test(e.message)) return badRequest("screenshot_delay must be < 0.8 * interval ms");
throw e;
} Prevention
- Recompute the bound whenever interval changes.
- Show a live max-delay hint derived from interval in the UI.
When it happens
Trigger: Save a real-browser monitor with a small interval and a large screenshot_delay, e.g. interval=20 (seconds) and screenshot_delay=16001 (>= 16000ms). The threshold recomputes from interval at validation time.
Common situations: User lowers the monitor interval after setting a delay and forgets to re-lower the delay. Default delay from a template that was tuned for a 60s interval applied to a 20s monitor. Mixing seconds (interval) and milliseconds (delay) mentally.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Screenshot delay must be less than ${maxDelayFromInterval}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/0604ffa6413c3a92.
Report an issue: GitHub.