grafana/k6 · error · InvalidSelectorError
Unexpected end of selector while parsing selector
Error message
Unexpected end of selector while parsing selector `${selector}` What it means
This error is returned by the k6 browser module's Frame evaluation of a handle (used by frame.evaluateHandle and internal handle lookups). It wraps the failure of EvalHandle on the frame's main-world execution context: either the execution context for mainWorld does not exist yet (the frame is navigating, loading, or detached) or the CDP Runtime.callFunctionOn evaluation itself failed. The source shows the code waits for the execution context, tries once, and wraps any failure with 'evaluating handle for frame'.
Solutions
- Wait for the frame to finish loading before evaluating: await page.waitForLoadState('load') (or frame.waitForLoadState) after goto().
- Re-acquire the frame from page.frames() to make sure it is still attached, then retry the evaluate call.
- Wrap the call in try/catch and retry once after a short delay to ride out navigation races.
- If the inner error says the page function threw, fix the JavaScript you pass to the evaluation.
Example fix
// before
await page.goto('https://example.com');
const handle = await page.evaluateHandle('document.body'); // may race navigation
// after
await page.goto('https://example.com');
await page.waitForLoadState('load');
const handle = await page.evaluateHandle('document.body'); Defensive patterns
Strategy: try-catch
Validate before calling
await page.waitForLoadState('load'); // ensure navigation finished before handle evaluation
const frames = page.frames();
if (!frames.some(f => f === page.mainFrame())) throw new Error('frame not available'); Try / catch
try {
const h = await page.evaluateHandle('document.body');
} catch (e) {
if (String(e).includes('evaluating handle for frame')) {
await page.waitForLoadState('load');
return await page.evaluateHandle('document.body'); // one retry after context settles
}
throw e;
} Prevention
- Always await waitForLoadState (or a selector) after goto() before evaluate/evaluateHandle.
- Never cache frame objects across navigations; re-fetch from page.frames().
- Keep evaluations on elements that provably exist (waitForSelector first).
When it happens
Trigger: Calling frame/page evaluateHandle-style APIs while the frame is mid-navigation or still loading (execution context not yet created); calling it right after page.goto() before the load event; calling it on a frame that was detached (iframe removed from DOM or its page closed); a JavaScript exception thrown inside the evaluated page function.
Common situations: Scripts that evaluate handles immediately after goto() without waiting for load state; acting on iframes that get removed/replaced by SPA rerenders; races between navigation and evaluation in reactive apps; running against slow CI hosts where context creation lags.
Related errors
- Error while parsing selector
- frame not found for id
- getProperties
- an error occurred communicating with k6 Cloud
- " " attribute is only supported for roles: "`).join(", ")}
AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18).
Data as JSON: /api/errors/24b1954559d08393.
Report an issue: GitHub.
Appendix: source
Thrown at internal/js/modules/k6/browser/common/js/injected_script.js:989
// packages/playwright-core/src/utils/isomorphic/cssParser.ts
var InvalidSelectorError = class extends Error {
};
// packages/playwright-core/src/utils/isomorphic/selectorParser.ts
function parseAttributeSelector(selector, allowUnquotedStrings) {
let wp = 0;
let EOL = selector.length === 0;
const next = () => selector[wp] || "";
const eat1 = () => {
const result2 = next();
++wp;
EOL = wp >= selector.length;
return result2;
};
const syntaxError = (stage) => {
if (EOL)
throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \`${selector}\``);
throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}` + (stage ? " during " + stage : ""));
};
function skipSpaces() {
while (!EOL && /\s/.test(next()))
eat1();
}
function isCSSNameChar(char) {
return char >= "\x80" || char >= "0" && char <= "9" || char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "_" || char === "-";
}
function readIdentifier() {
let result2 = "";
skipSpaces();
while (!EOL && isCSSNameChar(next()))
result2 += eat1();
return result2;
}
function readQuotedString(quote) {
let result2 = eat1();View on GitHub (pinned to 3fcf5388d7)