n8n-io/n8n · error · Error
No monitors available
Error message
No monitors available
What it means
Thrown by getPrimaryMonitor() in @n8n/computer-use when node-screenshots' MonitorClass.all() returns an empty array, meaning the host exposes no display devices. The function is used to pick a primary monitor for screen-capture tooling. The error is a bare `new Error(...)` (no n8n error class), so callers must match on message text.
Source
Thrown at packages/@n8n/computer-use/src/tools/monitor-utils.ts:6
import type { Monitor } from 'node-screenshots';
export async function getPrimaryMonitor(): Promise<Monitor> {
const { Monitor: MonitorClass } = await import('node-screenshots');
const monitors = MonitorClass.all();
if (monitors.length === 0) throw new Error('No monitors available');
return monitors.find((m) => m.isPrimary()) ?? monitors[0];
}
View on GitHub (pinned to 5ac6606e81)
Solutions
- Run the host with an attached display or start a virtual framebuffer: `xvfb-run -s '-screen 0 1920x1080x24' <cmd>`.
- If running in Docker, add a virtual display (`apt-get install xvfb` then launch Xvfb) or use an image that ships one.
- Confirm node-screenshots can enumerate monitors in isolation before invoking the tool: `import('node-screenshots').then(m => console.log(m.Monitor.all().length))`.
- Guard the call site: if getPrimaryMonitor() is optional, wrap it in try/catch and degrade gracefully or surface a user-facing 'no display' message.
- On Linux, ensure libx11, libxcb, libxrandr and a running X server (or Wayland compositor) are present.
Example fix
// before
const monitor = await getPrimaryMonitor();
// after
let monitor;
try {
monitor = await getPrimaryMonitor();
} catch (err) {
if (err instanceof Error && err.message === 'No monitors available') {
throw new OperationalError('Screen capture is unavailable: no display detected. Run with an attached or virtual (Xvfb) display.');
}
throw err;
} Defensive patterns
Strategy: validation
Validate before calling
import('node-screenshots').then(({ Monitor }) => {
const count = Monitor.all().length;
if (count === 0) {
// surface 'no display' to the user; do not call getPrimaryMonitor()
}
}); Type guard
function hasDisplay(monitors: unknown[]): monitors is { isPrimary(): boolean }[] {
return Array.isArray(monitors) && monitors.length > 0;
} Try / catch
try {
const monitor = await getPrimaryMonitor();
} catch (err) {
if (err instanceof Error && err.message === 'No monitors available') {
// degrade or instruct user to attach/Xvfb a display
} else throw err;
} Prevention
- In headless environments, start Xvfb before invoking screen-capture tools.
- Pre-flight check display availability via node-screenshots before exposing the tool to users.
- Document that the computer-use tool requires a display; gate feature flags on its presence.
When it happens
Trigger: Calling getPrimaryMonitor() on a headless server, a container without a virtual framebuffer (no Xvfb), a CI runner with no attached display, or an SSH session without X forwarding. Also triggered on OSes where node-screenshots cannot enumerate screens (missing native deps).
Common situations: Running the computer-use tool inside Docker/k8s without a display server; CI environments; remote SSH-only boxes; a misconfigured Xvfb; a Linux box where libx11/libxcb native libraries are absent so node-screenshots returns [].
Related errors
- ${toolName} was registered without a runSubAgent callback, a
- No cancellation handler is available for "${request.subAgent
- Gateway rejected token: ${status} ${body}
- Failed to upload capabilities: ${response.status} ${text}
- Credential creation failed: ${res.status} ${text}
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/c31bf9948249e083.
Report an issue: GitHub.