grafana/k6 · error

parse isVisible options of selector

Error message

parse isVisible options of selector %q: %w

What it means

Thrown when `frame.isVisible(selector, opts)` options fail to parse into FrameIsVisibleOptions — struct contains only `strict` (no timeout; visibility is checked immediately). Parse is currently coercive, so this branch cannot fire; unknown keys like `timeout` are silently ignored.

Solutions

  1. Use only { strict: bool } for isVisible
  2. For waiting behavior use frame.waitForSelector with state: 'visible' and a timeout
  3. Keep option objects minimal to avoid silent key dropping

Example fix

// before
await frame.isVisible('#el', { timeout: 5000 }); // timeout ignored

// after
await frame.waitForSelector('#el', { state: 'visible', timeout: 5000 });
Defensive patterns

Strategy: try-catch

Validate before calling

const opts = { strict: false };
if ('timeout' in opts) console.warn('isVisible does not support timeout; it returns immediately');

Type guard

function isVisibleOptions(o) {
  return o == null || (typeof o === 'object' && (o.strict === undefined || typeof o.strict === 'boolean'));
}

Try / catch

try {
  const visible = await frame.isVisible(sel, { strict: true });
} catch (e) {
  if (String(e).includes('parse isVisible options')) { /* fix opts */ } else { throw e; }
}

Prevention

When it happens

Trigger: No current input triggers it; only a validating Parse would. A `timeout` key is dropped without error.

Common situations: Expecting isVisible to wait/retry like an assertion — it is an instantaneous check; users porting waitFor patterns onto isVisible hit logic bugs rather than this error.

Related errors


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

Appendix: source

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

				return nil, fmt.Errorf("parse isEnabled options of selector %q: %w", selector, err)
			}
			return promise(vu, func() (any, error) {
				return f.IsEnabled(selector, popts) //nolint:wrapcheck
			}), nil
		},
		"isHidden": func(selector string, opts sobek.Value) (*sobek.Promise, error) {
			popts, err := parseFrameIsHiddenOptions(rt, opts)
			if err != nil {
				return nil, fmt.Errorf("parse isHidden options of selector %q: %w", selector, err)
			}
			return promise(vu, func() (any, error) {
				return f.IsHidden(selector, popts) //nolint:wrapcheck
			}), nil
		},
		"isVisible": func(selector string, opts sobek.Value) (*sobek.Promise, error) {
			popts, err := parseFrameIsVisibleOptions(rt, opts)
			if err != nil {
				return nil, fmt.Errorf("parse isVisible options of selector %q: %w", selector, err)
			}
			return promise(vu, func() (any, error) {
				return f.IsVisible(selector, popts) //nolint:wrapcheck
			}), nil
		},
		"locator": func(selector string, opts sobek.Value) mapping {
			return mapLocator(vu, f.Locator(selector, parseLocatorOptions(rt, opts)))
		},
		"frameLocator": func(selector string) *sobek.Object {
			mfl := mapFrameLocator(vu, f.FrameLocator(selector))
			return rt.ToValue(mfl).ToObject(rt)
		},
		"name": f.Name,
		"page": func() mapping {
			return mapPage(vu, f.Page())
		},
		"parentFrame": func() mapping {
			return mapFrame(vu, f.ParentFrame())

View on GitHub (pinned to 8d06114777)