grafana/k6 · error

parsing wait for selector

Error message

parsing wait for selector %q options: %w

What it means

Thrown while parsing the options object of frame.waitForSelector(selector, options) (browser/frame_mapping.go:461); the selector is echoed via %q. FrameWaitForSelectorOptions.Parse has a hard enum check on state and fails with '"%s" is not a valid DOM state' unless state is exactly one of attached, detached, visible or hidden (common/frame.go domElementStateToID). strict and timeout are coerced and cannot fail.

Solutions

  1. Use one of the four lowercase states: attached | detached | visible | hidden
  2. Trim and lowercase dynamic values: state: String(raw).trim().toLowerCase() after validating against the whitelist
  3. Omit state (defaults to visible)
  4. Add an early assertion in your script when state comes from external data

Example fix

// before
frame.waitForSelector('#el', { state: 'Visible' });
// after
frame.waitForSelector('#el', { state: 'visible' });
Defensive patterns

Strategy: validation

Validate before calling

const DOM_STATES = ['attached', 'detached', 'visible', 'hidden'];
if (opts?.state !== undefined && !DOM_STATES.includes(opts.state)) {
  throw new Error(`state must be one of ${DOM_STATES.join('|')}, got '${opts.state}'`);
}

Type guard

function isWaitForSelectorOptions(o) {
  if (o == null) return true;
  return (o.state === undefined || ['attached', 'detached', 'visible', 'hidden'].includes(o.state)) &&
    (o.timeout === undefined || typeof o.timeout === 'number');
}

Try / catch

try { const h = await frame.waitForSelector(sel, opts); } catch (e) { if (!String(e.message).includes('is not a valid DOM state')) throw e; }

Prevention

When it happens

Trigger: frame.waitForSelector('#el', { state: 'Visible' }) (wrong casing); { state: 'attached ' } (trailing whitespace); { state: 'displayed' } or 'present' (invented states); state loaded from config with stray characters.

Common situations: Autocomplete-free editors letting casing typos through; translating Selenium expectations (displayed/present) into Playwright-style states; values arriving from CSV/JSON data sources without trimming.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of grafana/k6@8d06114777 (2026-09-14). Data as JSON: /api/errors/1472e21eea3ad448. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/browser/browser/frame_mapping.go:459

			}), nil
		},
		"waitForLoadState": func(state string, opts sobek.Value) (*sobek.Promise, error) {
			popts, err := parseFrameWaitForLoadStateOptions(rt, opts, f.Timeout())
			if err != nil {
				return nil, fmt.Errorf("parsing waitForLoadState %q options: %w", state, err)
			}

			return promise(vu, func() (any, error) {
				return nil, f.WaitForLoadState(state, popts) //nolint:wrapcheck
			}), nil
		},
		"waitForNavigation": func(opts sobek.Value) (*sobek.Promise, error) {
			return mapWaitForNavigation(rt, vu, f, opts)
		},
		"waitForSelector": func(selector string, opts sobek.Value) (*sobek.Promise, error) {
			popts, err := parseFrameWaitForSelectorOptions(rt, opts, f.Timeout())
			if err != nil {
				return nil, fmt.Errorf("parsing wait for selector %q options: %w", selector, err)
			}

			return promise(vu, func() (any, error) {
				eh, err := f.WaitForSelector(selector, popts)
				if err != nil {
					return nil, err //nolint:wrapcheck
				}
				return mapElementHandle(vu, eh), nil
			}), nil
		},
		"waitForTimeout": func(timeout int64) *sobek.Promise {
			return promise(vu, func() (any, error) {
				f.WaitForTimeout(timeout)
				return nil, nil
			})
		},
		"waitForURL": func(url sobek.Value, opts sobek.Value) (*sobek.Promise, error) {
			return mapWaitForURL(rt, vu, f, url, opts)

View on GitHub (pinned to 8d06114777)