microsoft/playwright · error · Error

options.visibility is not supported, did you mean options.st

Error message

options.visibility is not supported, did you mean options.state?

What it means

waitForSelector rejects the legacy options.visibility property. It was renamed to options.state; if a caller passes visibility, Playwright throws with the hint 'did you mean options.state?'. (visibility === 'visible' is silently tolerated via the state path, but any other value triggers this.)

Source

Thrown at packages/playwright-core/src/server/frames.ts:840

  async evaluateExpressionHandle(progress: Progress, expression: string, options: { isFunction?: boolean, world?: types.World } = {}, arg?: any): Promise<js.JSHandle<any>> {
    return await progress.race(this._evaluateExpressionHandle(expression, options, arg));
  }

  private async _evaluateExpressionHandle(expression: string, options: { isFunction?: boolean, world?: types.World } = {}, arg?: any): Promise<js.JSHandle<any>> {
    const context = await this.context(options.world ?? 'main');
    const value = await context.evaluateExpressionHandle(expression, options, arg);
    return value;
  }

  async querySelector(progress: Progress, selector: string, options: types.StrictOptions): Promise<dom.ElementHandle<Element> | null> {
    this.apiLog(`    finding element using the selector "${selector}"`);
    return progress.race(this.selectors.query(selector, options));
  }

  async waitForSelector(progress: Progress, selector: string, performActionPreChecksAndLog: boolean, options: types.WaitForElementOptions, scope?: dom.ElementHandle): Promise<dom.ElementHandle<Element> | null> {
    if ((options as any).visibility)
      throw new Error('options.visibility is not supported, did you mean options.state?');
    if ((options as any).waitFor && (options as any).waitFor !== 'visible')
      throw new Error('options.waitFor is not supported, did you mean options.state?');
    const { state = 'visible' } = options;
    if (!['attached', 'detached', 'visible', 'hidden'].includes(state))
      throw new Error(`state: expected one of (attached|detached|visible|hidden)`);
    if (performActionPreChecksAndLog)
      progress.log(`waiting for ${this._asLocator(selector)}${state === 'attached' ? '' : ' to be ' + state}`);
    const promise = this.retryWithProgressAndBackoff(progress, async (progress, continuePolling) => {
      if (performActionPreChecksAndLog)
        await this._page.performActionPreChecks(progress);

      if (scope && await progress.race(scope.evaluateInUtility(([injected, node]) => node.isConnected, {})) !== true)
        throw new dom.NonRecoverableDOMError('Element is not attached to the DOM');

      const resolved = await progress.race(this.selectors.callOnSelectorHandle(selector, { ...options, scope }, ({ injected, elements }) => {
        const element: Element | undefined  = elements[0];
        const visible = element ? injected.utils.isElementVisible(element) : false;
        let log = '';

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Rename visibility to state with the matching value: 'visible'|'hidden'|'attached'|'detached'.
  2. Remove the property entirely if 'visible' (the default) is what you want.

Example fix

// before
await page.waitForSelector('#x', { visibility: 'hidden' }); // throws
// after
await page.waitForSelector('#x', { state: 'hidden' });
Defensive patterns

Strategy: validation

Validate before calling

// Normalize legacy options before calling
if ('visibility' in opts) { opts.state = opts.visibility; delete opts.visibility; }
await page.waitForSelector(sel, opts);

Type guard

function isWaitForSelectorOpts(o: any): o is { state?: string } {
  return !('visibility' in o);
}

Try / catch

null

Prevention

When it happens

Trigger: Calling waitForSelector (or any API built on it) with options.visibility set to something other than the implicit 'visible' default, e.g. { visibility: 'hidden' } or { visibility: 'attached' }.

Common situations: Carrying over options from old Playwright versions or other libraries (e.g. Puppeteer's waitForSelector visibility); copy-pasted snippets using the pre-rename property name.

Related errors


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