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

  1. Verify the selector matches exactly one element: add data-testid attributes or use more specific selectors, and prefer page/frame locator APIs.
  2. Wait for the element before filling: await page.waitForSelector(sel, { state: 'visible' }) or locator(sel).waitFor().
  3. Pass an explicit timeout that fits the app: page.fill(sel, value, { timeout: 60000 }).
  4. If the control is a custom widget (not a real input), use click/type or frame.type instead of fill.
  5. 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

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


AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18). Data as JSON: /api/errors/9e2de5db4c3b9024. Report an issue: GitHub.