microsoft/playwright · error · NonRecoverableDOMError
Element is outside of the viewport
Error message
Element is outside of the viewport
What it means
In the same retry loop, a result of 'error:notinviewport' means the element is visible but its bounding box does not intersect the viewport. With force:true or noAutoWaiting the loop cannot retry, so it throws NonRecoverableDOMError('Element is outside of the viewport'). Without force, Playwright would scroll the element into view and retry.
Solutions
- Remove force:true so Playwright scrolls the element into view automatically.
- Explicitly scroll first: await locator.scrollIntoViewIfNeeded().
- Use force only when the element is already in the viewport.
Example fix
// before
await page.locator('#footer-link').click({ force: true }); // off-screen → throws
// after
await page.locator('#footer-link').scrollIntoViewIfNeeded();
await page.locator('#footer-link').click(); Defensive patterns
Strategy: retry
Validate before calling
// Drop force so Playwright scrolls into view, or scroll explicitly. await locator.scrollIntoViewIfNeeded(); await locator.click();
Prevention
- Do not use force on off-screen elements; let Playwright scroll-into-view.
- Call scrollIntoViewIfNeeded() before forced actions on long pages.
- Verify the element is in the viewport with locator.isInViewport() (where available).
When it happens
Trigger: Calling a pointer action with { force: true } on an element that is rendered but scrolled out of the viewport; element in a long page below the fold where force suppresses the automatic scroll-into-view.
Common situations: Long pages with force-click on off-screen buttons; elements in scrollable containers where the test disabled the scroll behavior via force.
Related errors
- Did not find some options
- Element is not
- Element is not visible
- intercepts pointer events
- Option being selected is not enabled
AI-assisted analysis of microsoft/playwright@f1d33b5029 (2026-08-12).
Data as JSON: /api/errors/2604add3243cd0a5.
Report an issue: GitHub.
Appendix: source
Thrown at packages/playwright-core/src/server/dom.ts:348
if (result === 'error:notconnected')
return result;
}
} else {
progress.log(`attempting ${actionName} action${options.trial ? ' (trial run)' : ''}`);
}
if (!options.skipActionPreChecks && !options.force && !noAutoWaiting)
await this._frame._page.performActionPreChecks(progress);
const result = await action(progress, retry);
++retry;
if (result === 'error:notvisible') {
if (options.force || noAutoWaiting)
throw new NonRecoverableDOMError('Element is not visible');
progress.log(' element is not visible');
continue;
}
if (result === 'error:notinviewport') {
if (options.force || noAutoWaiting)
throw new NonRecoverableDOMError('Element is outside of the viewport');
progress.log(' element is outside of the viewport');
continue;
}
if (result === 'error:optionsnotfound') {
if (noAutoWaiting)
throw new NonRecoverableDOMError('Did not find some options');
progress.log(' did not find some options');
continue;
}
if (result === 'error:optionnotenabled') {
if (noAutoWaiting)
throw new NonRecoverableDOMError('Option being selected is not enabled');
progress.log(' option being selected is not enabled');
continue;
}
if (typeof result === 'object' && 'hitTargetDescription' in result) {
if (noAutoWaiting)
throw new NonRecoverableDOMError(`${result.hitTargetDescription} intercepts pointer events`);View on GitHub (pinned to f1d33b5029)