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
- Rename visibility to state with the matching value: 'visible'|'hidden'|'attached'|'detached'.
- 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
- Use state, never visibility, on waitFor-style APIs.
- Audit copy-pasted snippets for the legacy property.
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
- options.waitFor is not supported, did you mean options.state
- Error while parsing selector `${selector}` - selector cannot
- Frame locators are not allowed inside composite locators, wh
- Composite locators are not supported with piercing frames, w
- state: expected one of (attached|detached|visible|hidden)
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/ff1d74789871f135.
Report an issue: GitHub.