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.
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 c8fc3bf8d3)
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
- Element is not visible
- Did not find some options
- ${result.hitTargetDescription} intercepts pointer events
- Element is not ${result.missingState}
- Option being selected is not enabled
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/2604add3243cd0a5.
Report an issue: GitHub.