{"record":{"id":"f8a5903bbe4dda83","repo":"grafana/k6","slug":"querying-selector-q","errorCode":null,"errorMessage":"querying selector %q","messagePattern":"querying selector %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":1243,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"querying selector %q: %w\", selector, err)\n\t}\n\tif result == nil {\n\t\treturn nil, nil //nolint:nilnil\n\t}\n\thandle, ok := result.(JSHandleAPI)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"querying selector %q, wrong type %T\", selector, result)\n\t}\n\telement := handle.AsElement()\n\tif element == nil {\n\t\tdefer func() {\n\t\t\tif err := handle.Dispose(); err != nil {\n\t\t\t\terr = fmt.Errorf(\"disposing element handle: %w\", err)\n\t\t\t\trerr = errors.Join(err, rerr)\n\t\t\t}\n\t\t}()\n\t\treturn nil, fmt.Errorf(\"querying selector %q\", selector)\n\t}\n\n\treturn element, nil\n}\n\n// QueryAll queries element subtree for matching elements.\n// If no element matches the selector, the return value resolves to \"null\".\nfunc (h *ElementHandle) QueryAll(selector string) ([]*ElementHandle, error) {\n\thandles, err := h.queryAll(selector, h.evalWithScript)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"querying all selector %q: %w\", selector, err)\n\t}\n\n\treturn handles, nil\n}\n\nfunc (h *ElementHandle) queryAll(selector string, eval evalFunc) (_ []*ElementHandle, rerr error) {\n\tparsedSelector, err := NewSelector(selector)","sourceCodeStart":1225,"sourceCodeEnd":1261,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/element_handle.go#L1225-L1261","documentation":"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.","triggerScenarios":"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.","commonSituations":"Scraping text with //text() axes through element.$; text= engine matching outside element boundaries in edge cases; assuming every selector result is an element.","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"],"exampleFix":"// before\nconst t = await el.$('xpath=.//h1/text()'); // text node -> not an element\nconst s = await t.textContent();\n// after\nconst h = await el.$('xpath=.//h1');\nconst s = await h.textContent();","handlingStrategy":"validation","validationCode":"// Prefer selectors that can only match elements\nconst isElementSelector = s => !/\\/text\\(\\)\\s*$/.test(s) && !/\\/comment\\(\\)\\s*$/.test(s);\nif (!isElementSelector(sel)) throw new Error('selector can match non-element nodes');","typeGuard":"// After the call: k6 returns null for no match; any non-null result of $() is an element\nconst isElement = r => r !== null && r !== undefined;","tryCatchPattern":"try { const t = await el.$(sel); }\ncatch (e) { if (/querying selector .*$/m.test(e.message)) { /* selector matched a non-element: adjust it */ } throw e; }","preventionTips":["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"],"tags":["selector","xpath","element-handle","browser"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}