grafana/k6 · error

%q is not a valid DOM state

Error message

%q is not a valid DOM state

What it means

FrameWaitForSelectorOptions.Parse (frame_options.go:759) looks up the `state` option in domElementStateToID (frame.go:52-57), which defines exactly four valid states: "attached", "detached", "visible", "hidden". An unknown string aborts waitForSelector with this error before any waiting happens. The check is case-sensitive local validation, not a DOM query result.

Source

Thrown at internal/js/modules/k6/browser/common/frame_options.go:759

		Strict:  false,
		Timeout: defaultTimeout,
	}
}

// Parse parses the frame waitForSelector options.
func (o *FrameWaitForSelectorOptions) Parse(ctx context.Context, opts sobek.Value) error {
	rt := k6ext.Runtime(ctx)

	if !common.IsNullish(opts) {
		opts := opts.ToObject(rt)
		for _, k := range opts.Keys() {
			switch k {
			case "state":
				state := opts.Get(k).String()
				if s, ok := domElementStateToID[state]; ok {
					o.State = s
				} else {
					return fmt.Errorf("%q is not a valid DOM state", state)
				}
			case "strict":
				o.Strict = opts.Get(k).ToBoolean()
			case "timeout":
				o.Timeout = time.Duration(opts.Get(k).ToInteger()) * time.Millisecond
			}
		}
	}

	return nil
}

// FrameDispatchEventOptions are options for Frame.dispatchEvent.
type FrameDispatchEventOptions struct {
	*FrameBaseOptions
}

// NewFrameDispatchEventOptions returns a new FrameDispatchEventOptions.

View on GitHub (pinned to 93accf6570)

Solutions

  1. Use one of: "attached", "detached", "visible", "hidden" (lowercase)
  2. Remember the default is "visible"; if you got the error you likely set an explicit invalid value
  3. Constrain state in shared constants/TS types so only the four values can be passed

Example fix

// before
frame.waitForSelector('.status', { state: 'shown' });

// after
frame.waitForSelector('.status', { state: 'visible' });
// or wait for removal from DOM:
frame.waitForSelector('.loading', { state: 'detached' });
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

function isDomState(v: unknown): v is 'attached' | 'detached' | 'visible' | 'hidden' {
  return v === 'attached' || v === 'detached' || v === 'visible' || v === 'hidden';
}

Prevention

When it happens

Trigger: frame.waitForSelector('#el', { state: 'shown' }), { state: 'Visible' }, { state: 'displayed' }, or values borrowed from other frameworks like 'attached-to-dom'. Default when omitted is "visible".

Common situations: Porting selectors logic from Selenium/Playwright vocabulary ('visible' vs 'displayed', 'attached' vs 'present'), or driving the state value from external test data that has not been constrained.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/001ea7903b9c1e32. Report an issue: GitHub.