If you see TypeError: Puppeteer is not a constructor, first check where the code is running and what your import actually returns. In Node.js, use the exported puppeteer instance and call methods such as launch() or connect()—do not write new Puppeteer(). In a browser page or Chrome extension, a Node package import is not enough: you need a browser-compatible bundle and the browser-specific Puppeteer setup for that environment.
The name puppeteer-web also appears in an older Chrome extension report from 2018. That setup and its suggested unsafe-eval workaround should not be treated as current general guidance. The current Puppeteer documentation describes a different browser and extension approach.
Why “Puppeteer Is Not a Constructor” happens
The error means JavaScript tried to construct a value that is not a constructor. In this case, the common problem is confusing Puppeteer’s exported API instance with the internal Puppeteer class, or using a Node-oriented package entrypoint in a browser runtime.
Current Puppeteer API documentation marks the Puppeteer class constructor as internal: application code should not call it directly or subclass it. In Node.js, the regular puppeteer package provides a PuppeteerNode instance, which extends the common Puppeteer API. Use that instance’s methods rather than constructing the class yourself.
#1 Best Overall
For a browser page or Chrome extension, the runtime matters as much as the import. The browser needs a compatible bundle and browser-specific puppeteer-core entrypoint; an ordinary Node package import is not a substitute.
Identify your runtime before changing code
Check the context where the failing line executes—not just where the project was authored. A build tool can bundle code for a browser even when the source files are JavaScript modules, and extension background code is still subject to the extension environment.
| Where the code runs | Use this approach | Important qualification |
|---|---|---|
| Node.js | Import or require puppeteer; call methods on the exported instance, such as launch() or connect(). |
Do not call new Puppeteer(). The Node export is documented as PuppeteerNode. |
| Ordinary browser page | Bundle for the browser and use puppeteer-core/lib/puppeteer/puppeteer-core-browser.js. |
Connect using a valid browser WebSocket endpoint. |
| Chrome extension | Follow Puppeteer’s extension setup: browser-compatible bundle, browser entrypoint, and ExtensionTransport over chrome.debugger. |
Extension support is experimental and the documented connection represents one page. |
Fix it in Node.js
If this is a Node program, use the package export as an object. The following CommonJS example launches a browser and closes it even if the work fails:
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
const puppeteer = require('puppeteer');
async function main() {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
For an ES module, the shape is similar:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
} finally {
await browser.close();
}
Use connect() instead of launch() when your program is meant to attach to an already-running browser and has the required connection details. The key correction is the same: call a method on the imported package instance, rather than attempting to instantiate a class called Puppeteer.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsCheck the imported value
If the error persists, inspect what your import resolves to before changing other code. For CommonJS, temporarily log puppeteer and typeof puppeteer; for an ES module, inspect the imported binding. Confirm your code is calling a method on the package export, not destructuring or renaming a value and then using it as a constructor. Also compare the import statement with the package entrypoint actually selected by your runtime and bundler.
Fix it in a normal browser page
A browser page cannot simply run the regular Node-oriented Puppeteer package import. Puppeteer’s browser guide calls for a browser-compatible bundle and the browser-specific puppeteer-core entrypoint:
Rank #3
puppeteer-core/lib/puppeteer/puppeteer-core-browser.js
Use your project’s bundler to produce a browser bundle that resolves that entrypoint. Then connect to a browser using a valid WebSocket endpoint. The high-level shape is:
import puppeteer from 'puppeteer-core/lib/puppeteer/puppeteer-core-browser.js';
const browser = await puppeteer.connect({
browserWSEndpoint: 'YOUR_BROWSER_WEBSOCKET_ENDPOINT'
});
Replace the endpoint with the actual WebSocket endpoint supplied by the browser you intend to control; the placeholder is not a usable connection value. If the browser-specific import cannot be resolved, check the installed package and bundler resolution rather than changing your page’s JavaScript to call new Puppeteer().
Fix it in a Chrome extension
Chrome extensions require a separate path from both Node.js and a regular browser page. Puppeteer’s official extension guide says support through chrome.debugger is experimental because the extension environment differs substantially from Node.js and has restricted Chrome DevTools Protocol access.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
- Bundle for the browser. Produce the browser-compatible bundle described by the extension guide instead of loading the old
puppeteer-web.jssetup as if it were a current general-purpose entrypoint. - Use the extension-specific browser entrypoint. The guide’s flow imports the browser entrypoint from
puppeteer-core, not the ordinary Node package export. - Connect through the extension transport. Use
ExtensionTransport.connectTab(tab.id)with the target tab’s ID, following the guide’s setup for the extension’schrome.debuggeraccess. - Plan for one page per connection. A connected Puppeteer browser represents one page in this extension flow. To work with another page, use
chrome.tabsto obtain its tab and establish another connection.
Because extension support is experimental and depends on browser-extension permissions and setup, do not treat a Node example as a drop-in extension solution. Likewise, do not add unsafe-eval just because an old answer suggested it. A 2018 Stack Overflow report described a Chrome 69 extension and received that recommendation, but it is historical advice for that reported setup; the current official guide documents the browser bundle and extension transport approach instead. The available current guidance does not establish a general need to change a manifest to permit unsafe-eval.
Use the old puppeteer-web report as context, not a recipe
The historical question described loading puppeteer/utils/browser/puppeteer-web.js in background.html, then calling require("puppeteer") in background.js. It reported the exception from the loaded bundle. That exact combination is a clue to investigate runtime and entrypoint mismatch, not proof that every modern occurrence has the same cause. Its Chrome 69 context and accepted answer do not establish the right fix for a current project.
Troubleshoot the remaining error
Once you have matched the approach to the runtime, work through these checks in order. The exact project-specific cause cannot be determined without its package version, import, bundler configuration, and full stack trace.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
- Confirm the execution context. Determine whether the failing script runs in Node, a normal page, an extension background context, or an extension page. Choose the corresponding setup above.
- Read the exact import. Search for
new Puppeteer, imports from internal paths, and code that assumes a CommonJS export is the same shape as an ES module default export. Replace direct class construction with the documented package instance and methods. - Check which package entrypoint the bundle selected. A browser build should resolve the browser-compatible entrypoint, not silently pull in the Node-oriented package path. If the import fails during bundling or execution, inspect the bundler’s resolution settings and output.
- Verify the installed package, not a guessed version. Check the dependency actually installed by the project and the lockfile. Do not apply version-specific advice until you know the version in use.
- For browser connections, validate the endpoint.
connect()needs a real, reachable browser WebSocket endpoint. A missing or invalid endpoint is a separate connection problem from trying to constructPuppeteer. - For extensions, check tab and debugger setup. Confirm the target tab ID and that the extension follows the documented
chrome.debuggerandExtensionTransportflow. Use another connection for another tab/page. - Use the complete stack trace. The top frame identifies where the failure is thrown; the import and runtime context help explain why that value was not constructible. Preserve the full trace when comparing your setup with the relevant official guide.
Or skip the browser setup
If your goal is to capture a website rather than build a browser-automation integration, ScreenshotNeo offers a one-request screenshot API. It accepts a URL and returns an image or PDF; its cleanup steps can accept cookie or consent banners and remove known consent platforms, newsletter popups, and chat widgets before capture. It reports whether a page was clean, blocked, blank, or failed, and only clean shots are billed; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. An MCP server provides screenshot and PDF tools for AI agents, and the free plan includes 1,000 shots a month without a card. See ScreenshotNeo for the service details.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Replace YOUR_API_KEY with your key. The API also supports options such as full-page capture, selector capture, viewport and device settings, PDF output, custom CSS or JavaScript, request blocking, caching, and asynchronous jobs. Check the ScreenshotNeo API documentation for the accepted parameters and response details. Paid plans start at $5 for 3,000 shots; AI agents can use the MCP server for screenshot and PDF tasks.
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
FAQ
Does puppeteer-web mean the same thing as Puppeteer for Node?
No. The old extension report used a browser-oriented file named puppeteer-web.js, while Node’s usual puppeteer import provides a Node API instance. Choose the entrypoint and connection method for the runtime actually executing your code.
Free tools Windows power users keep installed
One-click scans. No signup required.
Can I use this error to identify my Puppeteer version?
No. The exception alone does not reveal the installed release or bundler resolution. Inspect the dependency and lockfile in the affected project.
Can an extension control more than one tab?
The documented extension connection represents one page. For another page, the guide’s approach is to use chrome.tabs and create another connection.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

