nwjs/nw.js · error · Error
nw.callStaticMethodSync(Screen, AddScreenChangeCallback) fai
Error message
nw.callStaticMethodSync(Screen, AddScreenChangeCallback) fails
What it means
On the first listener, Screen.on calls the native AddScreenChangeCallback and expects a truthy result. If the native side returns false (callback registration failed in the C++ layer), a plain Error is thrown. Unlike the validation errors, this reflects an environment/platform failure rather than bad arguments. The `return` after the throw is dead code.
Source
Thrown at src/api/screen/screen.js:46
// Override the addListener method.
Screen.prototype.on = Screen.prototype.addListener = function(ev, callback) {
if ( ev != "displayBoundsChanged" && ev != "displayAdded" && ev != "displayRemoved" && ev != "chooseDesktopMedia")
throw new TypeError('only following event can be listened: displayBoundsChanged, displayAdded, displayRemoved');
var onRemoveListener = function (type, listener) {
if (this._numListener > 0) {
this._numListener--;
if (this._numListener == 0) {
process.EventEmitter.prototype.removeListener.apply(this, ["removeListener", onRemoveListener]);
nw.callStaticMethodSync('Screen', 'RemoveScreenChangeCallback', [ this.id ]);
}
}
};
if(this._numListener == 0) {
if (nw.callStaticMethodSync('Screen', 'AddScreenChangeCallback', [ this.id ])[0] == false ) {
throw new Error('nw.callStaticMethodSync(Screen, AddScreenChangeCallback) fails');
return;
}
process.EventEmitter.prototype.addListener.apply(this, ["removeListener", onRemoveListener]);
}
// Call parent.
process.EventEmitter.prototype.addListener.apply(this, arguments);
this._numListener++;
}
// Route events.
Screen.prototype.handleEvent = function(ev) {
if (ev != "chooseDesktopMedia")
arguments[1] = JSON.parse(arguments[1]);
// Call parent.
this.emit.apply(this, arguments);
}
View on GitHub (pinned to e15da848e9)
Solutions
- Run in an environment with a real display (or Xvfb on Linux CI).
- Defer Screen listener registration until after the window/main module has loaded.
- Wrap the call in try/catch and degrade gracefully (log and skip the feature).
Example fix
// before
nw.Screen.on('displayAdded', syncDisplays); // may throw in headless CI
// after
try {
nw.Screen.on('displayAdded', syncDisplays);
} catch (e) {
if (/AddScreenChangeCallback/.test(e.message)) {
console.warn('Screen observer unavailable; multi-monitor features disabled.');
} else {
throw e;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Cannot validate a native failure up front; ensure a display exists instead.
function hasDisplay() {
try { return nw.Screen && !!nw.Screen.screens; } catch (e) { return false; }
} Try / catch
try {
nw.Screen.on('displayAdded', fn);
} catch (e) {
if (e instanceof Error && /AddScreenChangeCallback/.test(e.message)) {
console.warn('Screen observer unavailable; skipping multi-monitor features.');
} else { throw e; }
} Prevention
- Run nw.js with a real display (Xvfb on Linux CI).
- Register screen listeners after the main window loads.
- Gracefully degrade features that depend on screen-change events.
When it happens
Trigger: Calling nw.Screen.on('displayBoundsChanged', fn) when the native screen-observer infrastructure failed to initialize — e.g., during very early startup, headless/CI environments without a display, or a platform where the screen-change observer is unsupported.
Common situations: Running nw.js in a headless/automated environment (CI, Docker without X11). Platform-specific observer failures (some Linux WMs, certain virtual displays). Resource exhaustion during startup.
Related errors
- only following event can be listened: displayBoundsChanged,
- Invaild parameter, need Shortcut object.
- It's forbidden to instantialize a Base class.
- Type of '{type}' is not supported
- Only support getting plain text from Clipboard
AI-assisted analysis of nwjs/nw.js@e15da848e9 (2026-08-13).
Data as JSON: /api/errors/fa524c4a9f1d9f48.
Report an issue: GitHub.