Running this code:
'use strict';
const puppeteer = require('puppeteer');
main().then(() => {
console.log('Done');
});
async function main() {
process.on('unhandledRejection', r => {
console.error(r);
process.exit(1);
});
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.google.com/', {
waitUntil: 'networkidle'
});
await page.waitForSelector('#i-dont-exist', {
timeout: 500
});
}
Will produce the stack trace:
Error: waiting failed: timeout 500ms exceeded
at Timeout.WaitTask._timeoutTimer.setTimeout (/Users/iftachbar/work/impactlabs/scrapper/node_modules/puppeteer/lib/FrameManager.js:410:58)
at ontimeout (timers.js:488:11)
at tryOnTimeout (timers.js:323:5)
at Timer.listOnTimeout (timers.js:283:5)
Which makes it very hard to debug the case.
The intuitive track trace I would expect should include the line where I run page.waitForSelector()
.
Maybe there is another solution I'm not thinking about...
FWIW, parent functions which set timeout callbacks are also excluded from stack trace:
'use strict';
main();
function main() {
setTimeout(() => { throw new Error('Error in a timeout'); }, 500);
}
Error: Error in a timeout
at Timeout.setTimeout [as _onTimeout] (test.js:6:28)
at ontimeout (timers.js:469:11)
at tryOnTimeout (timers.js:304:5)
at Timer.listOnTimeout (timers.js:264:5)
I solved it with a monkey patch. Not the best solution out there, but it's a must if you have a big system and you need to debug where the wait got an error:
async function catchTimeoutErrors(callback) {
const error = new Error('Got timeout');
try {
return await callback();
} catch (e) {
if (e.stack.indexOf('Timeout.WaitTask._timeoutTimer.setTimeout') >= 0) {
error.stack += '\nCaused by ' + e.stack;
throw error;
}
throw e;
}
}
async function patchPuppeteer() {
const browser = await puppeteer.launch({
headless: true
});
const page = await browser.newPage();
const mainFrame = page.mainFrame();
const originalWaitForFunction = mainFrame.__proto__.waitForFunction;
mainFrame.__proto__.waitForFunction = function waitForFunction() {
return catchTimeoutErrors(() => originalWaitForFunction.apply(this, arguments));
};
await browser.close();
}
Wrote it for version 0.10.1, didn't test it with 0.11.0 yet.
@barnash there are multiple of places where puppeteer is asynchronous and where stack traces would be confusing.
The solution would be node.js collecting async stacktraces; all other solutions won't work good enough in general case.
Most helpful comment
I solved it with a monkey patch. Not the best solution out there, but it's a must if you have a big system and you need to debug where the wait got an error:
Wrote it for version 0.10.1, didn't test it with 0.11.0 yet.