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
- Change the selector to match the element node itself: '//div' instead of '//div/text()'
- Use text content APIs (textContent()) on the element instead of selecting text nodes
- Verify in devtools that $$(selector) highlights an element, not a text node
- 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
- Never select text/comment nodes with $(); read textContent() on the element instead
- Verify selectors highlight elements in devtools
- Prefer the CSS engine for element-only guarantees
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
- element is not attached to the DOM
- getting document element: nil document
- clicking the checkbox did not change its state
- provided selector is empty
- only one of the selectors can capture using * modifier
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/f8a5903bbe4dda83.
Report an issue: GitHub.