microsoft/playwright · error · InvalidSelectorError

Unknown engine "${name}" while parsing selector ${stringifyS

Error message

Unknown engine "${name}" while parsing selector ${stringifySelector(parsed)}

What it means

Thrown as an InvalidSelectorError by Selectors.parseSelector when a selector string references an engine name that is neither a builtin (_builtinEngines) nor a custom engine previously registered via register(). Visit-all-selector-parts encounters the unknown prefix and aborts parsing before any lookup runs.

Source

Thrown at packages/playwright-core/src/server/selectors.ts:84

    this._engines.set(engine.name, engine);
  }

  testIdAttributeName(): string {
    return this._testIdAttributeName;
  }

  setTestIdAttributeName(testIdAttributeName: string) {
    this._testIdAttributeName = testIdAttributeName;
  }

  parseSelector(selector: string | ParsedSelector, strict: boolean) {
    const parsed = typeof selector === 'string' ? parseSelector(selector) : selector;
    let needsMainWorld = false;
    visitAllSelectorParts(parsed, part => {
      const name = part.name;
      const custom = this._engines.get(name);
      if (!custom && !this._builtinEngines.has(name))
        throw new InvalidSelectorError(`Unknown engine "${name}" while parsing selector ${stringifySelector(parsed)}`);
      if (custom && !custom.contentScript)
        needsMainWorld = true;
      if (this._builtinEnginesInMainWorld.has(name))
        needsMainWorld = true;
    });
    return {
      parsed,
      world: needsMainWorld ? 'main' as const : 'utility' as const,
      strict,
    };
  }
}

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Register the custom engine on the same Selectors instance/context before using it in selectors.
  2. Correct the typo — use a builtin name (css, xpath, text, role, etc.) if that was intended.
  3. Ensure the engine is registered once at startup, before any page.querySelector/locator call uses it.

Example fix

// before
await page.locator('myEngine(div)').click();

// after
await selectors.register({ name: 'myEngine', query: () => {...} });
await page.locator('myEngine(div)').click();
Defensive patterns

Strategy: validation

Validate before calling

const BUILTIN = ['css','xpath','text','role','nth','visible','id','aria-ref'];
function isKnownEngine(selectors: any, name: string): boolean {
  return BUILTIN.includes(name) || name.startsWith('internal:') || selectors._engines.has(name);
}

const engineName = selector.split(/[(:=]/)[0];
if (!isKnownEngine(selectors, engineName))
  throw new Error(`Unknown selector engine: ${engineName}. Register it first.`);

Type guard

function isResolvableSelector(selectors: any, selector: string): boolean {
  try { selectors.parseSelector(selector, true); return true; }
  catch { return false; }
}

Try / catch

try {
  await page.locator(selector).click();
} catch (e) {
  if (e instanceof Error && /Unknown engine/.test(e.message))
    throw new Error(`Selector uses an unregistered engine: ${selector}`);
  throw e;
}

Prevention

When it happens

Trigger: Using a selector like 'myEngine(div)' or 'prefix=foo' where 'myEngine'/'prefix' was never registered; a typo in a builtin name ('role' vs 'roll'); referencing a custom engine registered in a different Selectors instance.

Common situations: Forgetting to call selectors.register before using the engine in a selector; typo in an engine prefix; engine registered on a different browser context than the one running the query.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/ded55a47d50872df. Report an issue: GitHub.