puppeteer/puppeteer · error · Error
Multiple deep combinators found in sequence.
Error message
Multiple deep combinators found in sequence.
What it means
Puppeteer's deep combinator '>>>' (and the P-selector grammar) treats two consecutive deep combinators, or a combinator at the start/end of a selector, as illegal — analogous to CSS rejecting 'a >> >> b'. pQuerySelectorAll parses the selector into a list and flags any run of more than one deep combinator in sequence, throwing this.
Source
Thrown at packages/puppeteer-core/src/injected/PQuerySelector.ts:269
): AwaitableIterable<Node> {
const selectors = JSON.parse(selector) as ComplexPSelectorList;
// If there are any empty elements, then this implies the selector has
// contiguous combinators (e.g. `>>> >>>>`) or starts/ends with one which we
// treat as illegal, similar to existing behavior.
if (
selectors.some(parts => {
let i = 0;
return parts.some(parts => {
if (typeof parts === 'string') {
++i;
} else {
i = 0;
}
return i > 1;
});
})
) {
throw new Error('Multiple deep combinators found in sequence.');
}
return domSort(
AsyncIterableUtil.flatMap(selectors, selectorParts => {
const query = new PQueryEngine(root, selectorParts);
void query.run();
return query.elements;
}),
);
};
/**
* Queries the given node for all nodes matching the given text selector.
*
* @internal
*/
export const pQuerySelector = async function (
root: Node,View on GitHub (pinned to d484e21c17)
Solutions
- Use exactly one '>>>' between two real selector fragments (e.g. 'div >>> span').
- Sanitize generated selectors to collapse/trim repeated '>>>' before passing to $/$$.
- Split a multi-level query into separate $() calls instead of chaining deep combinators.
- Validate the selector string with a regex like /(^>>>|>>>\s*$|>>>\s*>>>)/ before use.
Example fix
// before
await page.$('div >>> >>> span');
// after
await page.$('div >>> span'); Defensive patterns
Strategy: validation
Validate before calling
function sanitizePSelector(sel: string): string {
// collapse repeated deep combinators and trim leading/trailing >>>
let s = sel.replace(/>>>(\s*>>>)+/g, '>>>').trim();
s = s.replace(/(^\s*>>>\s+|\s+>>>\s*$)/g, '');
if (/(>>>\s*>>>)/.test(s)) throw new Error('Invalid deep combinator sequence');
return s;
} Type guard
const hasValidCombinators = (sel: string): boolean => !/(^>>>|>>>\s*$|>>>\s*>>>)/.test(sel.trim());
Try / catch
try {
await page.$(selector);
} catch (e) {
if (e instanceof Error && e.message === 'Multiple deep combinators found in sequence.') {
selector = selector.replace(/>>>\s*>>>/g, '>>>'); // retry once
}
throw e;
} Prevention
- Never concatenate selector fragments with a trailing '>>>'.
- When building selectors programmatically, trim and dedupe '>>>'.
- Prefer splitting complex deep queries into multiple $() calls.
When it happens
Trigger: Writing a P-selector with '>>> >>>' or '>>>>' (two deep combinators in a row), or one that begins/ends with >>>. Typically from dynamically-built selector strings where fragments concat into '>>> >>>'.
Common situations: String-concatenating selector parts without trimming '>>>'; templating engines injecting an empty fragment between two '>>>'; copy-paste from docs that double the combinator.
Related errors
- At least one query method must be defined.
- Unknown selector type: ${selector.name}
- Unsupported browser '${browser.name}'
- Unsupported platform '${platform}'
- No pinned version found for ${browserName}
AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12).
Data as JSON: /api/errors/1bb515b7daaab016.
Report an issue: GitHub.