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

  1. Create the browser context with hasTouch enabled: const context = await browser.newContext({ hasTouch: true }).
  2. Use page.click() instead of page.tap() if touch emulation is not needed — click works in all contexts.
  3. 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

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


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/013f26df0daeb384. Report an issue: GitHub.