microsoft/playwright · error · Error
The page does not support tap. Use hasTouch context option t
Error message
The page does not support tap. Use hasTouch context option to enable touch support.
What it means
Thrown by Frame.tap() when the browser context was not created with hasTouch: true. Tap requires touch event emulation which is a context-level setting, not a per-page or per-action setting. The method checks browserContext._options.hasTouch before delegating to the element handle's _tap method.
Source
Thrown at packages/playwright-core/src/server/frames.ts:1295
position: options.sourcePosition,
});
}));
// Note: do not perform locator handlers checkpoint to avoid moving the mouse in the middle of a drag operation.
dom.assertDone(await this._retryWithProgressIfNotConnected(progress, target, { ...options, performActionPreChecks: false }, async (progress, handle) => {
return handle._retryPointerAction(progress, 'move and up', false, async (progress, point) => {
await this._page.mouse.move(progress, point.x, point.y, { steps: options.steps });
await this._page.mouse.up(progress);
}, {
...options,
waitAfter: 'disabled',
position: options.targetPosition,
});
}));
}
async tap(progress: Progress, selector: string, options: types.PointerActionWaitOptions) {
if (!this._page.browserContext._options.hasTouch)
throw new Error('The page does not support tap. Use hasTouch context option to enable touch support.');
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (progress, handle) => handle._tap(progress, options)));
}
async fill(progress: Progress, selector: string, value: string, options: types.CommonActionOptions) {
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (progress, handle) => handle._fill(progress, value, options)));
}
async focus(progress: Progress, selector: string, options: types.StrictOptions & { noAutoWaiting?: boolean }) {
dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (progress, handle) => handle._focus(progress)));
}
async blur(progress: Progress, selector: string, options: types.StrictOptions & { noAutoWaiting?: boolean }) {
dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options, (progress, handle) => handle._blur(progress)));
}
async resolveSelector(progress: Progress, selector: string, options: { mainWorld?: boolean } = {}): Promise<{ resolvedSelector: string }> {
const element = await progress.race(this.selectors.query(selector, options));
if (!element)View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Create the browser context with hasTouch enabled: const context = await browser.newContext({ hasTouch: true }).
- Use page.click() instead of page.tap() if touch emulation is not needed — click works in all contexts.
- Use devices descriptors that include hasTouch, e.g., browser.newContext({ ...devices['iPhone 13'] }).
Example fix
// before
const context = await browser.newContext();
await page.tap('#button'); // throws [323]
// after
const context = await browser.newContext({ hasTouch: true });
const page = await context.newPage();
await page.tap('#button'); Defensive patterns
Strategy: validation
Validate before calling
const context = await browser.newContext({ hasTouch: true });
// or validate before tapping
function assertHasTouch(page) {
if (!page.context()._options.hasTouch)
throw new Error('Enable hasTouch on context before using tap');
} Type guard
function contextSupportsTouch(context: import('@playwright/test').BrowserContext): boolean {
return (context.options() as any).hasTouch === true;
} Prevention
- Create context with { hasTouch: true } when testing touch interactions.
- Use device descriptors (e.g., devices['iPhone 13']) which include hasTouch.
- Use page.click() instead of page.tap() if touch emulation is unnecessary.
When it happens
Trigger: Calling page.tap(selector) or locator.tap() on a context/page created with the default options (hasTouch defaults to false). Common when test code originally written for a mobile/touch project is run in a standard desktop context without the hasTouch flag.
Common situations: Reusing a shared browser context fixture across desktop and mobile test suites without enabling hasTouch. Copying tap-based test code from a mobile emulation example into a standard context. Forgetting to set hasTouch when emulating specific mobile devices with browser.newContext().
Related errors
- hasTouch must be enabled on the browser context before using
- Frame has been detached.
- Invalid timezone ID: ${timezoneId}
- Unable to retrieve content because the page is navigating an
- Provide an object with a `url`, `path` or `content` property
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/013f26df0daeb384.
Report an issue: GitHub.