apify/crawlee · error · Error
Cannot activate BrowserController without an assigned browse
Error message
Cannot activate BrowserController without an assigned browser.
What it means
BrowserController.activate() is the entry point that marks a controller as active after a browser has been attached. The library throws this error when activate() is called while the internal `this.browser` field is still unset, because there is nothing to activate. It is a lifecycle-order guard: the controller must first have a browser assigned (via assignBrowser) before activation makes sense.
Source
Thrown at packages/browser-pool/src/abstract-classes/browser-controller.ts:180
#hasBrowserPromise = new Promise<void>((resolve) => {
this.#commitBrowser = resolve;
});
constructor(browserPlugin: BrowserPlugin<Library, LibraryOptions, LaunchResult, NewPageOptions, NewPageResult>) {
super();
this.log = serviceLocator.getLogger().child({ prefix: 'BrowserPool' });
this.browserPlugin = browserPlugin;
}
/**
* Activates the BrowserController. If you try to open new pages before
* activation, the pages will get queued and will only be opened after
* activate is called.
* @ignore
*/
activate(): void {
if (!this.browser) {
throw new Error('Cannot activate BrowserController without an assigned browser.');
}
this.#activate();
this.isActive = true;
}
/**
* @ignore
*/
assignBrowser(
browser: LaunchResult,
launchContext: LaunchContext<Library, LibraryOptions, LaunchResult, NewPageOptions, NewPageResult>,
): void {
if (this.browser) {
throw new Error('BrowserController already has a browser instance assigned.');
}
this.browser = browser;
this.launchContext = launchContext;
this.#commitBrowser();View on GitHub (pinned to dbe57fb09c)
Solutions
- Do not call activate() yourself — use BrowserPool.newPage() or newPageInNewBrowser() which handle assignment and activation in the correct order.
- If you must manage the controller manually, call controller.assignBrowser(launchResult, launchContext) before activate().
- Check that your custom plugin's _launch implementation actually returns/commits a browser so assignBrowser is reached.
Example fix
// before const controller = new PlaywrightBrowserController(options); controller.activate(); // throws // after const controller = new PlaywrightBrowserController(options); controller.assignBrowser(await playwright.launch(), launchContext); controller.activate();
Defensive patterns
Strategy: validation
Validate before calling
function canActivate(controller) { return controller.browser != null && !controller.isActive; }
if (!canActivate(controller)) throw new Error('assign a browser before activate()'); Type guard
function hasBrowser(c) { return c.browser !== undefined && c.browser !== null; } Prevention
- Never call activate() directly; go through BrowserPool.newPage()/newPageInNewBrowser()
- Always pair manual controller use with assignBrowser() first
- Keep lifecycle order: construct -> assignBrowser -> activate
When it happens
Trigger: Calling browserController.activate() directly (it is public despite the @ignore tag) on a freshly constructed controller, or on a controller that was never assigned a LaunchResult — e.g. instantiating a controller subclass manually instead of going through BrowserPool.newPage()/newPageInNewBrowser().
Common situations: Developers subclassing BrowserController and managing the lifecycle by hand; calling internal-ish methods out of order; plugins with custom _launch logic that never commit a browser before activation; version changes where activation used to implicitly launch.
Related errors
- BrowserController already has a browser instance assigned.
- This crawler instance is already running, you can add more r
- fetchNextRequest called on an uninitialized crawler
- The `response` property is not available. This might mean th
- The `gotoOptions` property is not available until `prepareNa
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/3f7c46b11440671a.
Report an issue: GitHub.