Puppeteer + BotBrowser: Launch and Profile Pitfalls
Avoid the Puppeteer mistakes that break BotBrowser profile consistency, including defaultViewport overrides, bundled Chromium, and browser-level CDP session mix-ups.
BotBrowser Team
Prefer the maintained product doc?
This article has a matching page in the docs center. Use the docs for the canonical setup flow, current flags, and long-term reference.
If you want the shortest possible BotBrowser and Puppeteer setup, the documentation page for Puppeteer is the place to start. This article stays with the part that usually goes wrong after the first successful launch: the three integration mistakes that quietly break profile consistency, how to recognize each one from its symptoms, and how to fix it without touching your page code.
Puppeteer is a control layer. It decides which binary starts, which launch options are applied, and which commands are sent over the DevTools connection. BotBrowser is the browser that loads a profile and presents it to pages. When the two are wired together correctly, your selectors, navigation logic, and data extraction run exactly as they did before. When they are wired together incorrectly, the profile can still load while something around it disagrees with it, and that disagreement is harder to diagnose than an outright crash.
Three launch mistakes that break profile consistency
Most failed Puppeteer integrations trace back to one of three causes. Each has a different symptom, so it helps to know them by what you see rather than by what you configured.
- The wrong binary starts. The regular
puppeteerpackage downloads its own Chromium build, and a launch without an explicitexecutablePathuses that build. The browser opens and pages load, but no BotBrowser profile is involved at all. - Puppeteer overrides the viewport. Puppeteer applies a default viewport of 800x600 to every new page unless you turn that behavior off. A profile that describes a larger display then sits next to a page that reports a small window, and the two numbers contradict each other.
- A BotBrowser command goes to the wrong session. BotBrowser adds its own CDP commands, and they are reachable on the browser-level session. A page-level session does not know them, so the call fails with a method-not-found error.
None of these produces an obvious crash. The first gives you a perfectly normal Chromium, the second gives you a normal page with an odd window size, and the third gives you one failed call that is easy to dismiss as a typo. That is why they survive code review so often.
Profile consistency simply means that values which belong together agree with each other. A session that reports a display, a window size, a language list, and a timezone should report combinations that fit each other. The profile supplies those values, so anything outside the profile that changes one of them, such as a Puppeteer option or a leftover script, creates a disagreement that the profile never intended.
A good launch has a short signature. The process that starts is the BotBrowser binary, the profile path is passed as a launch argument, the default viewport is switched off, and any BotBrowser-specific command is sent through a session created from the browser target. The sections below walk through each of those four points and then show how to confirm them from the outside.
Keep in mind what this checklist is and is not. It covers launch configuration: which binary, which arguments, which viewport behavior, which session. It does not change how Puppeteer drives pages, and it does not make claims about how any particular website will treat the resulting session.
Launch BotBrowser with puppeteer-core
Install the pieces
The BotBrowser documentation lists four prerequisites for Puppeteer: an installed BotBrowser binary, Node.js 18 or newer, the puppeteer-core package, and a profile file. Production setups use an encrypted .enc profile, and the profile must match the BotBrowser version you run.
- A BotBrowser binary, downloaded from the BotBrowser repository and installed as described in the installation guide.
- Node.js 18 or newer.
- The
puppeteer-corenpm package. - A profile file, normally with the
.encextension.
The package choice matters more than it looks. The full puppeteer package bundles a browser download, which is exactly the thing you do not want to launch by accident. puppeteer-core provides the same automation API without any bundled browser, so it forces you to say which executable to start. That requirement is the integration point: you pass the path of the BotBrowser binary, and Puppeteer starts it.
npm init -y
npm install puppeteer-core
A minimal launch
The documented minimal launch sets four things that a stock Puppeteer script usually omits or leaves at their defaults.
import puppeteer from 'puppeteer-core';
const browser = await puppeteer.launch({
browser: 'chrome',
executablePath: process.env.BOTBROWSER_EXEC_PATH,
headless: true,
defaultViewport: null,
args: [`--bot-profile=${process.env.BOT_PROFILE_PATH}`],
});
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
Each option has a job.
browser: "chrome"selects Chrome-mode startup, which is what the BotBrowser documentation asks for withpuppeteer-core. If a launch fails insidepuppeteer-core, this option and the executable path are the first two things to check.executablePathpoints at the BotBrowser binary. Reading it from an environment variable keeps machine-specific paths out of the script and makes the same code work on a laptop and on a server.headless: trueruns without a visible window. Switch it off while you debug if you want to watch the session.defaultViewport: nullturns off Puppeteer's viewport override, which the next section explains.argscarries the BotBrowser flags.--bot-profileloads the profile, and an absolute path avoids surprises when the working directory differs between machines.
The page code after the launch call is ordinary Puppeteer. Nothing in newPage, goto, selectors, or close is BotBrowser-specific, which is the point: moving an existing project over should only touch the launch block.
Why defaultViewport must be null
Puppeteer's launch options reference describes defaultViewport as the setting that gives every page a consistent viewport, with a default of 800x600. Setting it to null disables that default. In a normal test suite this is a convenience. With a BotBrowser profile it becomes a conflict, because the profile already describes the display and window dimensions the session should report.
When the override stays in place, Puppeteer sizes each page to 800x600 after the browser has loaded the profile. The profile says one thing and the page measures another. Anyone who compares the screen dimensions with the inner window dimensions will see a combination that no real device produces. This is the most common reason a first test run looks wrong even though the profile loaded without errors.
The fix is a single line, and the symptom is easy to confirm: if the inner window of a new page measures 800x600, the default viewport is still active. Set defaultViewport: null, launch again, and the page takes its dimensions from the profile instead. Keep the line in every launch call you write, including scripts you copy from older Puppeteer examples, because most of those examples rely on the default.
Move an existing project over
An existing Puppeteer project needs three edits, and all three happen in the launch block. Replace the puppeteer dependency with puppeteer-core, so that no bundled browser is downloaded and every launch has to name its executable. Replace the launch options with the minimal launch shown above, filling in the BotBrowser binary path and the profile path. Then remove any defaultViewport value and be deliberate about explicit viewport calls, since sizing a page by hand overrides the same profile dimensions.
Everything after the launch block stays as it is: navigation, waiting, selectors, data extraction, screenshots, and cleanup. If a project has several launch sites, such as one in a test helper and one in a standalone tool, update each of them. A single leftover launch with the old configuration is enough to bring back the bundled browser or the viewport override.
Close what you launch. Every call to puppeteer.launch starts a separate browser process, so wrap the work in try and finally and call browser.close() in the finally block, otherwise a failed run leaves a process behind.
Keep machine-specific values out of the source. The binary path, the profile path, and proxy credentials work well as environment variables, as in the examples above, because the same script then runs unchanged on a laptop, a CI runner, and a server, and credentials stay out of version control.
Send BotBrowser CDP commands on a browser-level session
Puppeteer exposes the Chrome DevTools Protocol through CDPSession objects. Standard protocol commands work in any session. BotBrowser's own commands, such as BotBrowser.setCustomHeaders, belong to the browser as a whole, so they are available on a session that is attached to the browser target.
const cdp = await browser.target().createCDPSession();
await cdp.send('BotBrowser.setCustomHeaders', {
headers: { 'x-client-app': 'bb' },
});
The call to look at is browser.target().createCDPSession(). It creates a session on the browser itself. The more familiar page.createCDPSession() creates a session on a single page target, and that session does not recognize BotBrowser commands. The result is a method-not-found error that reads like a typo in the command name, even when the name is exactly right.
When you see that error, check where the session came from before you check the spelling. If the session came from a page, create a new one from the browser target and send the command again. One browser-level session is enough for the whole script, so you can create it once after launch and reuse it for every BotBrowser command.
Naming makes the scope visible in code review. A variable called browserCdp, created from browser.target(), is hard to confuse with a page session, while a generic variable called client, created inside a page helper, is easy to misuse months later by someone who did not write it.
Standard Puppeteer features stay on their normal path. Only the BotBrowser-specific commands need the browser-level session, and everything else in the script keeps using the usual page methods.
Add proxy, timezone, and language settings
A profile describes a device. Network and regional settings describe where that device appears to be. The BotBrowser documentation passes both kinds of settings through args, next to --bot-profile.
const browser = await puppeteer.launch({
browser: 'chrome',
executablePath: process.env.BOTBROWSER_EXEC_PATH,
headless: true,
defaultViewport: null,
args: [
`--bot-profile=${process.env.BOT_PROFILE_PATH}`,
'--proxy-server=socks5://user:[email protected]:1080',
'--bot-timezone=Asia/Tokyo',
'--bot-locale=ja-JP',
'--bot-languages=ja-JP,ja,en-US,en',
],
});
Pass the proxy as --proxy-server in args. BotBrowser accepts credentials inside the proxy URL, and the documentation advises against framework-only proxy authentication patterns, because geographic values derived from the exit address will not line up if the proxy is configured outside the browser. If the timezone does not match the proxy region, look at how the proxy was configured before you look at anything else; Proxy Configuration covers the credential and syntax forms in more detail.
With a proxy set, the documented default is that BotBrowser derives timezone, locale, and languages from the proxy exit address. The explicit --bot-timezone, --bot-locale, and --bot-languages flags override those derived values for the settings they define. Use them when you want a fixed region, and leave them out when you want the values to follow the proxy. Several of these flags belong to higher BotBrowser tiers, so check the CLI flags reference for the tier that applies to your setup before relying on them.
If you run several browsers from one machine, give each one its own --user-data-dir. The documentation lists conflicts between instances as a known problem and the unique directory as the fix, because it keeps storage and session state separate.
Per-context proxy through browser.createBrowserContext() deserves a careful reading of your versions. Whether a proxy option is accepted there depends on the Puppeteer version and on the BotBrowser tier, so treat it as something to test on your own setup, not as something every combination supports.
Verify the launch and troubleshoot common failures
Verification should come from the page side, because that is what a website sees. Open a page, read a handful of values, and compare them with the profile you loaded.
const page = await browser.newPage();
await page.goto('https://example.com');
const reported = await page.evaluate(() => ({
userAgent: navigator.userAgent,
languages: navigator.languages,
screenWidth: screen.width,
screenHeight: screen.height,
innerWidth: window.innerWidth,
innerHeight: window.innerHeight,
pixelRatio: window.devicePixelRatio,
}));
console.log(reported);
Read the output in three steps. The user agent and language list should match what the profile and your regional flags describe. The screen dimensions should match the profile display. The inner window dimensions should be consistent with that display and should not be exactly 800x600. If they are 800x600, return to the launch block and look for the viewport override.
This snippet is a sanity check for your own integration, not a test suite and not a promise about any site. Compare the output with the profile you loaded, not with your own machine. The point of the check is that the values come from the profile, so a perfect match with the host would be a reason to look again. Run it once after a change to the launch block, and again when you move the script to another machine, since a different binary path or a copied example is where most regressions enter.
When something is off, the symptom usually points straight to the cause.
| Symptom | Likely cause and fix |
|---|---|
| The viewport measures 800x600 | The default viewport is active. Set defaultViewport: null. |
| The launch fails inside puppeteer-core | Add browser: "chrome" and confirm that executablePath points to the BotBrowser binary. |
| The profile does not seem to apply | The wrong binary may have started. Confirm the executable path and use an absolute profile path. |
| BotBrowser commands return method not found | The session came from a page. Create it with browser.target().createCDPSession(). |
| Timezone or languages do not match the proxy | Pass the proxy through --proxy-server in args, not through a framework-only option. |
| Several instances interfere with each other | Give each instance a unique --user-data-dir. |
If the profile does not seem to apply at all, work from the outside in; the flag combinations used in these examples are collected in CLI recipes. Confirm that the process you started is the BotBrowser binary and not a stock Chromium, confirm that the profile path is absolute and the file exists, and confirm that the profile package matches the BotBrowser major version you installed, because the documentation asks for matching versions. Fix one thing at a time and relaunch, so you know which change mattered.
A last question people ask is whether plugins that patch the page from JavaScript are still needed. They are not part of the documented BotBrowser setup. The profile is applied by the browser binary you launch, and the launch checklist above does not depend on page-level patches. Adding them gives you one more layer to reason about when a launch looks wrong, so test the documented configuration first and add anything else only for a reason you can name.
What BotBrowser covers and what it does not
BotBrowser documents Puppeteer launch through puppeteer-core with browser set to chrome, an executablePath, --bot-profile, and defaultViewport: null, so the profile's viewport dimensions stay authoritative instead of Puppeteer's 800x600 default, and BotBrowser CDP commands are sent on a browser-level session. For you, that means a short, checkable launch recipe: if a session looks inconsistent, you can compare it against these four points and find the one that differs. BotBrowser cannot fix a launch that Puppeteer overrides, such as a defaultViewport left at its default, it cannot replace correct Puppeteer launch configuration, and it does not guarantee how any target site treats the session.
The same limits apply to the neighboring topics. Proxy and regional flags depend on the proxy and on your BotBrowser tier, and per-context proxy support depends on both the Puppeteer version and the tier. Comparing BotBrowser with page-level patch plugins is only a statement about where each approach works, since one acts through the browser binary and the other through injected JavaScript. It says nothing about how any site will react to either.
For related setups, read Getting Started with Playwright if you also use Playwright, CLI Recipes for flag combinations, Headless Server Setup for running on a server, and Proxy Configuration for proxy syntax and credentials.
Public sources
This article is part of the guide: Browser Automation Getting Started: A First Run Map
Related Articles
Browser Automation Getting Started: A First Run Map
Getting StartedPlaywright with Profile-Backed Browser Identity
Getting StartedBrowser Automation CLI Recipes: Practical Command Examples
Getting StartedBotBrowser Launcher: Profiles, Proxies, and Setup
Getting StartedSelenium Browser Profile Integration and Session Ownership
Getting StartedBotBrowser Profiles: Formats, Rotation, and Common Failures
Take BotBrowser from research to production
The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.