If Pyppeteer’s JavaScript coverage is missing code or reporting unexpected ranges, first check when coverage starts, what page activity you recorded, and how navigation and anonymous scripts are handled. Start coverage before the code you want to measure runs, exercise the relevant routes and interactions, and inspect each returned script’s URL, source text, and ranges. These are common measurement and interpretation issues; the available documentation does not establish one universal Pyppeteer defect or a specific version regression.
Start with a correctly timed capture
Coverage can only describe execution observed during its recording window. Start it before navigation or before the script activity under investigation. Starting after a page has already loaded cannot reliably recover code that ran earlier.
The V8 JavaScript protocol documentation cautions: “Coverage data for JavaScript executed before enabling precise code coverage may be incomplete.” Precise coverage also affects execution: enabling it resets execution counters and prevents optimized code from running. Treat the result as an instrumented capture, not as a perfectly neutral observation of ordinary runtime behavior. V8/DevTools Protocol coverage documentation
Minimal capture pattern
This example measures a navigation and a follow-up interaction, then prints the returned entries. Replace the URL and interaction with the page flow you need to investigate.
Recommended Free Tools
#1 Best Overall
import asyncio
from pyppeteer import launch
async def main():
browser = await launch()
page = await browser.newPage()
await page.coverage.startJSCoverage()
try:
await page.goto("https://example.com", {"waitUntil": "networkidle2"})
# Exercise the route or interaction whose code you want to measure.
# For example: await page.click("button[data-action='open']")
coverage = await page.coverage.stopJSCoverage()
finally:
await browser.close()
for entry in coverage:
print("URL:", entry["url"])
print("Source characters:", len(entry["text"]))
print("Executed ranges:", entry["ranges"])
asyncio.run(main())
In normal use, stop coverage after the intended actions finish. If an exception can occur during navigation or interaction, use a cleanup path that stops coverage when possible before closing the browser, so you can still inspect what was captured. Avoid assuming that a page-load-only capture represents later routes, menu states, authenticated screens, or other user paths that were never visited.
Check whether navigation cleared earlier coverage
Pyppeteer 0.0.25 documents resetOnNavigation as defaulting to True. With that setting, navigation may clear accumulated coverage. If your workflow navigates between pages and expects one combined result, this default is an important diagnostic check. Pyppeteer API reference
You can request that coverage not reset on navigation:
await page.coverage.startJSCoverage({"resetOnNavigation": False})
Do not treat this option as a guarantee that coverage survives every navigation. The related Puppeteer documentation warns that browser architecture can still cause resets even when the option is false; Pyppeteer compatibility is not guaranteed by that documentation. Test the exact navigation flow in the browser build you use. Puppeteer CoverageOptions documentation
Rank #2
Decide what you want the capture to represent
- One document or route: Start coverage immediately before loading that route and stop after its relevant interactions.
- Several routes: Record each route explicitly, or test whether your exact navigation sequence retains the data you need. Do not infer cross-navigation persistence from the option alone.
- Single-page-app transitions: Trigger client-side navigation and interactions while the same recording remains active, then verify the resulting entries.
Include anonymous and dynamically generated scripts
Pyppeteer’s reportAnonymousScript option defaults to False. As a result, code created without an associated URL—such as some code evaluated through eval or new Function—may not appear in the output by default. Enable anonymous reporting when that code is part of the question:
await page.coverage.startJSCoverage({"reportAnonymousScript": True})
Pyppeteer uses the singular spelling reportAnonymousScript. Reported anonymous scripts are labeled __pyppeteer_evaluation_script__. Code with a source URL can be attributed and reported under that URL. If an expected entry is absent, check whether the code is anonymous and whether the option was enabled before it ran. Pyppeteer API reference
Read the entries and ranges without double-counting
stopJSCoverage() returns a list of entries containing a script URL, source text, and executed ranges. Look at all three together. An entry with an unexpected URL may be an attribution issue; missing source text or URL can also result in an entry being skipped by the implementation. An empty range list is not the same thing as a missing script entry.
Pyppeteer normalizes function coverage ranges into sorted, disjoint ranges. The documented offsets are half-open intervals, [start, end): the start offset is included and the end offset is excluded. Use the returned offsets consistently, and do not add overlapping raw ranges as if each represented distinct source characters. When calculating covered source length, sum the lengths of the disjoint ranges rather than counting shared spans twice.
Inspect a single result
for entry in coverage:
text = entry["text"]
ranges = entry["ranges"]
covered = sum(r["end"] - r["start"] for r in ranges)
print(entry["url"], "covered offsets:", covered, "source characters:", len(text))
for r in ranges:
print(" start:", r["start"], "end:", r["end"])
This arithmetic is useful for understanding character-offset spans in these returned ranges; it is not automatically a byte count, a percentage of meaningful application code, or a result that can be transferred to another tool without checking that tool’s offset conventions. Bundlers, source maps, generated scripts, and wrapper code can also make raw script text differ from the source files developers edit.
Make the captured page flow representative
Coverage is a record of what was loaded and exercised during a session, not a complete inventory of everything an application could execute. Chrome DevTools’ Coverage workflow similarly records a reload and subsequent interactions; its report depends on the resources and behavior observed during that recording. Chrome DevTools Coverage guide
Before deciding that a function is unused or a script is missing, include the relevant conditions in the capture:
- Load the route that owns the code, including direct visits as well as in-app navigation if both matter.
- Trigger menus, dialogs, lazy-loaded sections, form validation, and other interactions that execute code conditionally.
- Use the authentication state, feature flags, viewport, and data state relevant to the behavior you are diagnosing.
- Wait for the target activity to finish before stopping coverage; a request or delayed callback that has not run yet cannot appear as executed.
- Repeat the same page flow when comparing tools or runs, rather than comparing a full interaction recording with a load-only capture.
Verify the browser build and reproduce before blaming a release
Pyppeteer’s documentation says it works best with its bundled Chromium. A separately configured executable may differ in DevTools Protocol behavior or version. Record the Pyppeteer version, Chromium version and executable path, operating system, navigation sequence, and interaction steps whenever results appear inconsistent. Reproduce with the bundled browser before attributing the discrepancy to Pyppeteer itself. The sources available do not identify a specific version regression or Chromium defect for this issue. Pyppeteer documentation
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Rank #4
Capture environment details in Python
import pyppeteer
print("Pyppeteer version:", pyppeteer.__version__)
browser = await launch()
print("Chromium version:", await browser.version())
print("Browser process:", browser.process.args[0] if browser.process else "unknown")
If your installed package does not expose the version attribute as shown, obtain the package version from the same Python environment that runs the script. Include the complete capture code and exact steps in a reproducible report; “coverage looks wrong” alone does not distinguish timing, navigation, attribution, range interpretation, or a browser difference.
Compare with Chrome DevTools carefully
DevTools Coverage can help determine whether a discrepancy comes from the Pyppeteer capture path or from the page flow, but it is not an automatic correctness oracle. Use the same browser build where possible, reload in the same way, exercise the same interactions, and compare the actual scripts and ranges. A mismatch can reflect different recording scope, script attribution, or tool behavior; the comparison alone does not prove a Pyppeteer bug.
Troubleshoot by symptom
| Symptom | Likely check | Action |
|---|---|---|
| Expected code is absent | Coverage began after that code ran, the path was not exercised, or a generated script was anonymous. | Start coverage earlier, repeat the intended interaction, and enable reportAnonymousScript for anonymous code. |
| Earlier route data disappears | resetOnNavigation defaults to true; navigation resets may also occur despite requesting false. |
Inspect the option and test the actual navigation sequence; capture routes separately if necessary. |
| Unexpected synthetic URL appears | The script is anonymous and reported through Pyppeteer’s synthetic evaluation label. | Associate a source URL where appropriate or recognize __pyppeteer_evaluation_script__ as anonymous-script attribution. |
| Covered total is larger than expected | Downstream code may count overlapping intervals or use the wrong offset interpretation. | Use the returned disjoint ranges and half-open offsets; do not count a shared span twice. |
| An entry has no usable source text or URL | The implementation skips entries when required source or attribution data is missing. | Inspect the underlying script source and whether it has a URL; capture the full entry before filtering it downstream. |
| Pyppeteer and DevTools disagree | The browser build, capture start, route, interactions, or attribution may differ. | Align the environment and page flow first; treat remaining disagreement as a clue, not proof of a library regression. |
| Coverage changes runtime behavior | Precise coverage instrumentation alters optimization and execution counters. | Use coverage to diagnose observed execution, and avoid treating an instrumented run as a performance benchmark. |
Or skip the browser setup
For a screenshot of the rendered page rather than JavaScript execution ranges, ScreenshotNeo offers a one-request screenshot API and an MCP server for AI agents. It does not replace Pyppeteer coverage: use the browser workflow above when you need executed script ranges.
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}`);
See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.
Free tools Windows power users keep installed
One-click scans. No signup required.
Sign up free for ScreenshotNeo and try 1,000 screenshots a month with no card.
Best Value
Frequently Asked Questions
Does Pyppeteer coverage show every line in my application?
No. It records scripts and execution observed during the capture session, not an inventory of every possible application path.
Is an empty range list proof that a JavaScript file was never used?
Not on its own. Confirm that the entry has usable source and attribution, and that the relevant code ran after coverage began.
Can ScreenshotNeo return Pyppeteer JavaScript coverage ranges?
No. ScreenshotNeo captures screenshots or PDFs; use Pyppeteer coverage when you need JavaScript execution ranges.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchQuick 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.




