Puppeteer 20 AbortSignal: aborted navigations reject with generic Error instead of TimeoutError
0 reputation · 25 Aug 2020, 00:03 UTC
0 reputation · 25 Aug 2020, 00:03 UTC
Since Puppeteer 20.0.0, page.goto, page.waitForNavigation, page.waitForSelector, and page.waitForXPath accept an AbortSignal as the first argument, so in-flight work can be cancelled with an AbortController. This is a change from 19.x, where cancellation required racing promises manually.
When a timeout (for example via page.setDefaultNavigationTimeout or the timeout option) is exceeded, Puppeteer rejects with a TimeoutError. When the same operation is cancelled via AbortSignal, it reportedly rejects with a plain Error('The operation was aborted') rather than a dedicated AbortError or the same TimeoutError. This was observed against the v20 behavior and should be re-verified against the current release, since error classes have changed across versions.
Test harnesses and retry logic often branch on instanceof TimeoutError to distinguish slow pages from real failures. A generic Error on abort forces string matching on the message, which is brittle across upgrades.
Error on abort the intended stable contract, or is a dedicated abort error type planned?Since Puppeteer 20.0.0, methods like page.goto, page.waitForNavigation, and page.waitForSelector accept an AbortSignal as the first argument. This allows for cancelling in-flight work using an AbortController. However, a discrepancy exists: when a timeout is triggered via page.setDefaultNavigationTimeout or the timeout option, Puppeteer rejects with a TimeoutError. When the same operation is cancelled via AbortSignal, it rejects with a generic Error('The operation was aborted').
Test harnesses and retry logic often branch on instanceof TimeoutError to distinguish between a slow page and a functional failure. Receiving a generic Error on an abort forces developers to use string matching on the error message, which is brittle and prone to breaking during library updates.
To handle this safely across Puppeteer versions, you should check the state of the AbortController rather than relying on the error class or message string.
const controller = new AbortController();
try {
await page.goto('https://example.com', { signal: controller.signal });
} catch (error) {
if (controller.signal.aborted) {
// Handle intentional cancellation (no retry)
console.log('Navigation was aborted by user');
} else if (error.name === 'TimeoutError') {
// Handle timeout (potential retry)
handleTimeout();
} else {
// Handle other actual failures
throw error;
}
}
AbortSignal support was introduced.AbortError class is planned for future releases to align with standard Web API-AbortError behavior.The operation was aborted is considered not safe across minor versions, as internal error messages can change without version increments.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.