grafana/k6 · error · InvalidSelectorError
Error while parsing selector `${selector}` - unexpected symb
Error message
Error while parsing selector `${selector}` - unexpected symbol "${next()}" at position ${wp} What it means
Frame.Fill() in the k6 browser module (internal/js/modules/k6/browser/common/frame.go:932) wraps every failure of the underlying fill action with 'filling %q with %q'. The action first waits for an element matching the selector to reach the attached state (respecting Timeout, default ~30s) and then calls the element's fill, which requires an <input>, <textarea>, or contenteditable element. Any timeout, strict-mode violation, or element-type failure is surfaced inside this wrapper.
Source
Thrown at internal/js/modules/k6/browser/common/js/injected_script.js:990
// 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();
if (result2 !== quote)View on GitHub (pinned to 01ffac6f24)
Solutions
- Verify the selector matches exactly one element: add data-testid attributes or use more specific selectors, and prefer page/frame locator APIs.
- Wait for the element before filling: await page.waitForSelector(sel, { state: 'visible' }) or locator(sel).waitFor().
- Pass an explicit timeout that fits the app: page.fill(sel, value, { timeout: 60000 }).
- If the control is a custom widget (not a real input), use click/type or frame.type instead of fill.
- Run with K6_DEBUG=true (or --log-output) to see the wrapped inner error naming the real cause.
Example fix
// before
await page.fill('#username', 'alice');
// after
const user = page.locator('#username');
await user.waitFor({ state: 'visible', timeout: 10000 });
await user.fill('alice'); Defensive patterns
Strategy: try-catch
Validate before calling
const sel = '#username';
const found = await page.waitForSelector(sel, { state: 'visible', timeout: 5000 });
if (!found) throw new Error('username field not rendered'); Try / catch
try {
await page.fill(sel, value, { timeout: 10000 });
} catch (e) {
const msg = String(e);
if (msg.includes('timed out')) throw new Error(`field ${sel} never appeared`);
if (msg.includes('input element')) throw new Error(`${sel} is not fillable; use type()`);
throw e;
} Prevention
- Prefer locators: const loc = page.locator(sel); await loc.waitFor(); await loc.fill(v).
- Add data-testid attributes in the app under test for stable selectors.
- Set explicit per-action timeouts sized to the app's render time instead of relying on the 30s default.
When it happens
Trigger: Selector matches nothing within opts.Timeout (default 30s, or the value set at launch/Context options); selector matches multiple elements with Strict:true; target element exists but is not an input/textarea/contenteditable (handle.fill returns a node-type error); element or its frame is detached mid-action (navigation during fill).
Common situations: Selectors written against one environment (data-testid present) failing on another; SPAs rendering inputs asynchronously after data fetches, so the element appears later than the timeout; iframes re-rendering during the fill; using Fill on a div or custom widget that only looks like an input.
Related errors
- Error while parsing selector `${selector}`: ${e.message}
- Error while parsing selector `${selector}` - cannot use ${op
- "${attr.name}" does not support "${attr.op}" matcher
- "name" attribute must have a value
- Unknown attribute "${attr.name}", must be one of ${kSupporte
AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18).
Data as JSON: /api/errors/9e2de5db4c3b9024.
Report an issue: GitHub.