apify/crawlee · error · Error
${crawlerName}: ${names.map((name) => `\`${name}\``).join(',
Error message
${crawlerName}: ${names.map((name) => `\`${name}\``).join(', ')} cannot be combined with `browserPool`, ${names.length > 1 ? 'they configure' : 'it configures'} the browser pool the crawler would build for itself. Configure the pool you pass in instead. What it means
Browser-based crawlers (CheerioCrawler aside) can accept an externally constructed browserPool. Options that configure the internally built pool (e.g. launchContext, browserPoolOptions, proxyConfiguration) are mutually exclusive with a user-supplied browserPool. assertBrowserPoolNotConfigured throws when both are provided.
Source
Thrown at packages/browser-crawler/src/internals/browser-crawler.ts:73
* The type of a browser pool the crawler builds (and therefore owns) for itself. It's an {@apilink IBrowserPool} that
* additionally exposes `destroy()` — the crawler only ever tears down pools it created, which is why {@apilink IBrowserPool}
* itself intentionally omits `destroy`.
*/
export type OwnedBrowserPool<Page> = IBrowserPool<Page> & { destroy: () => Promise<void> };
/**
* Rejects options that exist only to configure the browser pool the crawler would have built for itself.
* Accepting them alongside a pre-built `browserPool` and quietly ignoring them is how `browserPoolOptions` grew
* into a second, half-working way of configuring the same pool.
*/
export function assertBrowserPoolNotConfigured(crawlerName: string, ignoredOptions: Dictionary): void {
const names = Object.keys(ignoredOptions).filter((name) => ignoredOptions[name] !== undefined);
if (names.length === 0) {
return;
}
throw new Error(
`${crawlerName}: ${names.map((name) => `\`${name}\``).join(', ')} cannot be combined with \`browserPool\`, ` +
`${names.length > 1 ? 'they configure' : 'it configures'} the browser pool the crawler would build for ` +
'itself. Configure the pool you pass in instead.',
);
}
type ContextDifference<T, U> = Omit<U, keyof T> & Partial<U>;
export interface BrowserCrawlingContext<
Page extends CommonPage = CommonPage,
Response extends BaseResponse = BaseResponse,
UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
GoToOptions extends Dictionary = Dictionary,
> extends CrawlingContext<UserData> {
/**
* The browser page object where the web page is loaded and rendered.
*/
page: Page;View on GitHub (pinned to dbe57fb09c)
Solutions
- Remove the conflicting options (launchContext, browserPoolOptions, proxyConfiguration, etc.) and configure the browserPool instance directly instead
- Or drop the browserPool argument and let the crawler build its own pool from those options
- Configure the passed-in pool before handing it to the crawler (hooks, plugins, proxies on the pool itself)
Example fix
// before
const crawler = new PuppeteerCrawler({ browserPool: myPool, launchContext: { launchOptions: { headless: true } } });
// after
myPool.hooks.onBrowserLaunched?.(...); // configure myPool itself
const crawler = new PuppeteerCrawler({ browserPool: myPool }); Defensive patterns
Strategy: validation
Validate before calling
const poolOptions = ['launchContext','browserPoolOptions','proxyConfiguration'].filter(k => k in options && options[k] !== undefined);
if ('browserPool' in options && poolOptions.length) throw new Error(`Options ${poolOptions} conflict with browserPool`); Prevention
- When passing browserPool, configure that pool instance directly
- Strip pool-configuring options from shared config objects when injecting a pool
When it happens
Trigger: Constructing e.g. new PuppeteerCrawler({ browserPool, launchContext: {...} }) or { browserPool, browserPoolOptions } / { browserPool, proxyConfiguration } - any pool-configuring option set alongside browserPool.
Common situations: Migrating from default construction to dependency-injected pools while leaving old launch/proxy options in place; copy-pasting crawler configs and adding browserPool.
Related errors
- Provided browserPlugin is not one of the plugins used by Bro
- Remote browser endpoint resolved to an empty string.
- Remote browser endpoint() must return a URL string or an obj
- Stagehand instance not found for browser
- Could not resolve StagehandController for page — is the brow
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/4d41f13bcd28788b.
Report an issue: GitHub.