microsoft/playwright · error · Error
Selector engine name may only contain [a-zA-Z0-9_] character
Error message
Selector engine name may only contain [a-zA-Z0-9_] characters
What it means
Thrown by Selectors.register when the engine name does not match the regex /^[a-zA-Z_0-9-]+$/. The guard ensures selector engine names are safe identifier characters before they are stored and used in selector parsing. Note: the error message text says [a-zA-Z0-9_] but the regex actually permits hyphens as well — a minor doc/string mismatch.
Source
Thrown at packages/playwright-core/src/server/selectors.ts:60
'internal:has', 'internal:has-not',
'internal:has-text', 'internal:has-not-text',
'internal:and', 'internal:or', 'internal:chain',
'role', 'internal:attr', 'internal:label', 'internal:text',
'internal:role', 'internal:testid', 'internal:describe',
'aria-ref'
]);
this._builtinEnginesInMainWorld = new Set([
'_react', '_vue',
]);
this._engines = new Map();
this._testIdAttributeName = testIdAttributeName ?? 'data-testid';
for (const engine of engines)
this.register(engine);
}
register(engine: channels.SelectorEngine) {
if (!engine.name.match(/^[a-zA-Z_0-9-]+$/))
throw new Error('Selector engine name may only contain [a-zA-Z0-9_] characters');
// Note: we keep 'zs' for future use.
if (this._builtinEngines.has(engine.name) || engine.name === 'zs' || engine.name === 'zs:light')
throw new Error(`"${engine.name}" is a predefined selector engine`);
if (this._engines.has(engine.name))
throw new Error(`"${engine.name}" selector engine has been already registered`);
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;View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Rename the engine to use only letters, digits, underscores, and hyphens.
- Sanitize the name with name.replace(/[^a-zA-Z0-9_-]/g, '') before registering.
- If you need namespacing, use a single hyphen (allowed) rather than a colon.
Example fix
// before
selectors.register({ name: 'my:engine', query: () => {...} });
// after
selectors.register({ name: 'my-engine', query: () => {...} }); Defensive patterns
Strategy: validation
Validate before calling
function sanitizeEngineName(name: string): string {
const cleaned = name.replace(/[^a-zA-Z0-9_-]/g, '');
if (!cleaned) throw new Error('Engine name is empty after sanitization');
return cleaned;
}
const name = sanitizeEngineName(rawName);
selectors.register({ name, query: () => {/*...*/} }); Type guard
function isValidEngineName(name: string): boolean {
return /^[a-zA-Z_0-9-]+$/.test(name);
} Prevention
- Validate engine names against /^[a-zA-Z_0-9-]+$/ before registering.
- Generate engine names from a controlled allow-list of characters.
- Avoid whitespace and punctuation (especially colons) in custom engine names.
When it happens
Trigger: Calling selectors.register(engine) with engine.name containing spaces, colons, dots, unicode, or any character outside [A-Za-z0-9_-]; e.g. registering a custom engine named 'my engine' or 'foo:bar'.
Common situations: User-generated or dynamic engine names that were not sanitized; copying an engine name that includes a namespace separator like ':'; whitespace accidentally included in the name string.
Related errors
- "${engine.name}" is a predefined selector engine
- "${engine.name}" selector engine has been already registered
- Unknown engine "${name}" while parsing selector ${stringifyS
- Invalid input image
- Invalid output dimensions
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/468136fa003b5ed7.
Report an issue: GitHub.