grafana/k6 · error

querying selector %q

Error message

querying selector %q

What it means

ElementHandle.Query() succeeded at the protocol level but handle.AsElement() returned nil: the matched remote object is not a DOM element (element_handle.go:1243). Unlike its neighbors this message has no wrapped error — it purely means 'the selector matched a non-element node'. The handle is disposed and the error returned.

Source

Thrown at internal/js/modules/k6/browser/common/element_handle.go:1243

	if err != nil {
		return nil, fmt.Errorf("querying selector %q: %w", selector, err)
	}
	if result == nil {
		return nil, nil //nolint:nilnil
	}
	handle, ok := result.(JSHandleAPI)
	if !ok {
		return nil, fmt.Errorf("querying selector %q, wrong type %T", selector, result)
	}
	element := handle.AsElement()
	if element == nil {
		defer func() {
			if err := handle.Dispose(); err != nil {
				err = fmt.Errorf("disposing element handle: %w", err)
				rerr = errors.Join(err, rerr)
			}
		}()
		return nil, fmt.Errorf("querying selector %q", selector)
	}

	return element, nil
}

// QueryAll queries element subtree for matching elements.
// If no element matches the selector, the return value resolves to "null".
func (h *ElementHandle) QueryAll(selector string) ([]*ElementHandle, error) {
	handles, err := h.queryAll(selector, h.evalWithScript)
	if err != nil {
		return nil, fmt.Errorf("querying all selector %q: %w", selector, err)
	}

	return handles, nil
}

func (h *ElementHandle) queryAll(selector string, eval evalFunc) (_ []*ElementHandle, rerr error) {
	parsedSelector, err := NewSelector(selector)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Change the selector to match the element node itself: '//div' instead of '//div/text()'
  2. Use text content APIs (textContent()) on the element instead of selecting text nodes
  3. Verify in devtools that $$(selector) highlights an element, not a text node
  4. Prefer the CSS engine when you only need elements

Example fix

// before
const t = await el.$('xpath=.//h1/text()'); // text node -> not an element
const s = await t.textContent();
// after
const h = await el.$('xpath=.//h1');
const s = await h.textContent();
Defensive patterns

Strategy: validation

Validate before calling

// Prefer selectors that can only match elements
const isElementSelector = s => !/\/text\(\)\s*$/.test(s) && !/\/comment\(\)\s*$/.test(s);
if (!isElementSelector(sel)) throw new Error('selector can match non-element nodes');

Type guard

// After the call: k6 returns null for no match; any non-null result of $() is an element
const isElement = r => r !== null && r !== undefined;

Try / catch

try { const t = await el.$(sel); }
catch (e) { if (/querying selector .*$/m.test(e.message)) { /* selector matched a non-element: adjust it */ } throw e; }

Prevention

When it happens

Trigger: An xpath that selects text or comment nodes ('xpath=//div/text()'), a selector engine resolving to a primitive/non-node value, or querying a document/window-ish object where an element was required.

Common situations: Scraping text with //text() axes through element.$; text= engine matching outside element boundaries in edge cases; assuming every selector result is an element.

Related errors


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