puppeteer/puppeteer · error · Error

At least one query method must be defined.

Error message

At least one query method must be defined.

What it means

CustomQuerySelectorRegistry.register requires a handler that defines at least one of queryOne or queryAll (it can derive the other). The final else-if catches the case where BOTH are missing/undefined and rejects the registration. This guards Puppeteer.registerCustomQueryHandler against useless handlers.

Source

Thrown at packages/puppeteer-core/src/injected/CustomQuerySelector.ts:41

  #selectors = new Map<string, CustomQuerySelector>();

  register(name: string, handler: CustomQueryHandler): void {
    if (!handler.queryOne && handler.queryAll) {
      const querySelectorAll = handler.queryAll;
      handler.queryOne = (node, selector) => {
        for (const result of querySelectorAll(node, selector)) {
          return result;
        }
        return null;
      };
    } else if (handler.queryOne && !handler.queryAll) {
      const querySelector = handler.queryOne;
      handler.queryAll = (node, selector) => {
        const result = querySelector(node, selector);
        return result ? [result] : [];
      };
    } else if (!handler.queryOne || !handler.queryAll) {
      throw new Error('At least one query method must be defined.');
    }

    this.#selectors.set(name, {
      querySelector: handler.queryOne,
      querySelectorAll: handler.queryAll!,
    });
  }

  unregister(name: string): void {
    this.#selectors.delete(name);
  }

  get(name: string): CustomQuerySelector | undefined {
    return this.#selectors.get(name);
  }

  clear(): void {
    this.#selectors.clear();

View on GitHub (pinned to d484e21c17)

Solutions

  1. Provide at least queryOne or queryAll in the handler passed to registerCustomQueryHandler.
  2. Double-check method names are exactly 'queryOne' and 'queryAll'.
  3. If you only have a multi-result query, register queryAll and Puppeteer will synthesize queryOne (and vice-versa).
  4. Add a compile-time type annotation (: CustomQueryHandler) to catch missing methods at build time.

Example fix

// before
Puppeteer.registerCustomQueryHandler('foo', {});

// after
Puppeteer.registerCustomQueryHandler('foo', {
  queryAll: (node, selector) => node.querySelectorAll(selector),
});
Defensive patterns

Strategy: validation

Validate before calling

function registerSafe(name: string, handler: Record<string, unknown>) {
  if (typeof handler.queryOne !== 'function' && typeof handler.queryAll !== 'function') {
    throw new Error(`Cannot register '${name}': provide queryOne and/or queryAll`);
  }
  Puppeteer.registerCustomQueryHandler(name, handler as any);
}

Type guard

const isValidHandler = (h: unknown): boolean =>
  !!h && (typeof (h as any).queryOne === 'function' ||
          typeof (h as any).queryAll === 'function');

Try / catch

try {
  Puppeteer.registerCustomQueryHandler('name', handler);
} catch (e) {
  if (e instanceof Error && e.message === 'At least one query method must be defined.') {
    // handler missing methods — log and skip registration
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling Puppeteer.registerCustomQueryHandler('name', {}) with an empty handler object, or with a handler whose queryOne and queryAll are both undefined (e.g. a typo in the method name).

Common situations: Typos like { queyOne: ... } (misspelled); passing a class instance that doesn't expose the expected methods; spreading a config that omits both keys.

Related errors


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