microsoft/playwright · error · Error
options.waitFor is not supported, did you mean options.state
Error message
options.waitFor is not supported, did you mean options.state?
What it means
waitForSelector rejects the legacy options.waitFor property (except the value 'visible', which maps onto the default). Any other waitFor value triggers the error with the hint to use options.state instead.
Source
Thrown at packages/playwright-core/src/server/frames.ts:842
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 = '';
if (elements.length > 1)
log = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`;View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Remove options.waitFor and set options.state to 'visible'|'hidden'|'attached'|'detached' as needed.
- If you actually wanted lifecycle, call page.waitForLoadState(...) separately.
Example fix
// before
await page.waitForSelector('#x', { waitFor: 'load' }); // throws
// after
await page.waitForSelector('#x', { state: 'visible' });
await page.waitForLoadState('load'); Defensive patterns
Strategy: validation
Validate before calling
// Strip/redirect the legacy key
if ('waitFor' in opts && opts.waitFor !== 'visible') {
// caller probably meant load-state; split the call
await page.waitForLoadState(opts.waitFor);
delete opts.waitFor;
} Type guard
function hasLegacyWaitFor(o: any): boolean { return 'waitFor' in o && o.waitFor !== 'visible'; } Try / catch
null
Prevention
- Keep element-state waits (waitForSelector) and lifecycle waits (waitForLoadState) separate.
- Do not pass lifecycle values into waitForSelector options.
When it happens
Trigger: Calling waitForSelector with options.waitFor set to a non-'visible' value (e.g. 'load', 'domcontentloaded', 'networkidle') - usually confused with page.waitForLoadState's waitUntil semantics.
Common situations: Mixing up waitForSelector's element-state option with waitForLoadState's lifecycle option; copy-pasting options across the two APIs; legacy code from an API change.
Related errors
- options.visibility is not supported, did you mean options.st
- 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/d3267f2547da0f02.
Report an issue: GitHub.