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

  1. Use exactly one '>>>' between two real selector fragments (e.g. 'div >>> span').
  2. Sanitize generated selectors to collapse/trim repeated '>>>' before passing to $/$$.
  3. Split a multi-level query into separate $() calls instead of chaining deep combinators.
  4. 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

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


AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12). Data as JSON: /api/errors/1bb515b7daaab016. Report an issue: GitHub.