Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsListen for Puppeteer’s console page event before the navigation or interaction that should produce the message. In the handler, match the message text or type, await page.screenshot(), and keep the browser open until that screenshot finishes. A timeout and a finally block make the script recover cleanly when the message never appears.
Complete example: capture after a matching console error
This runnable ES module waits for a console message containing target phrase and having type error. The listener is installed before page.goto(), so a message emitted during navigation cannot be missed.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const screenshotAfterMessage = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Timed out waiting for console message'));
}, 10_000);
page.on('console', async msg => {
if (msg.type() !== 'error' || !msg.text().includes('target phrase')) {
return;
}
clearTimeout(timeout);
try {
const image = await page.screenshot({ path: 'after-console.png' });
resolve(image);
} catch (error) {
reject(error);
}
});
});
await page.goto('https://example.com');
await screenshotAfterMessage;
} finally {
await browser.close();
}
Install Puppeteer with npm install puppeteer, save the file as an ES module (for example, use a .mjs extension), and run it with Node.js. The resulting after-console.png is written in the process’s current directory. Puppeteer’s screenshot API is Page.screenshot(); when the path ends in .png, the extension determines the output format.
Why the listener must come first
Console events are emitted as the page runs. If the page logs during navigation, register the listener before page.goto(). If a click, form submission, timer, or injected script is the trigger, register it before that action instead:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows 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 reinstall#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
const screenshotAfterMessage = waitForConsoleScreenshot(page, {
type: 'warn',
text: 'payment form ready',
path: 'form-ready.webp'
});
await page.click('#open-payment-form');
await screenshotAfterMessage;
Installing the handler after the click or navigation cannot recover an event that has already been emitted.
Match the message you actually need
Match by type
msg.type() identifies categories such as error, warn, log, and info. Type-only matching is useful when any error should trigger the capture:
page.once('console', async msg => {
if (msg.type() === 'error') {
await page.screenshot({ path: 'first-error.png' });
}
});
Match exact or partial text
msg.text() returns the rendered message text. Use strict equality when the text is stable, or includes() (or a regular expression) when the page adds variable details such as an ID or URL:
const wanted = /checkout failed|payment declined/i;
page.on('console', async msg => {
if (msg.type() === 'error' && wanted.test(msg.text())) {
await page.screenshot({ path: 'checkout-error.png' });
}
});
Combining type and text avoids taking a screenshot for an unrelated warning that happens to contain the same word.
Rank #2
- 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
Use one event or many
Use page.once('console', handler) when the first console event is the only event of interest. For a persistent listener, use page.on('console', handler) and filter inside it. A persistent listener can produce multiple captures if the page logs the matching message repeatedly, so add a guard when only one image is wanted:
let captured = false;
page.on('console', async msg => {
if (captured || msg.type() !== 'error' || !msg.text().includes('target phrase')) return;
captured = true;
await page.screenshot({ path: 'first-matching-error.png' });
});
A reusable wait function with timeout and cleanup
For test suites and scripts that perform several captures, isolate the event handling in a helper. This version supports type, substring matching, a custom timeout, and a screenshot path. It removes the listener on success or failure so it does not leak into the next test.
function screenshotWhenConsole(page, {
type,
text,
path = 'console-triggered.png',
timeout = 10_000,
fullPage = false
} = {}) {
return new Promise((resolve, reject) => {
let timer;
const cleanup = () => {
page.off('console', onConsole);
clearTimeout(timer);
};
const onConsole = async msg => {
if (type && msg.type() !== type) return;
if (text && !msg.text().includes(text)) return;
cleanup();
try {
const result = await page.screenshot({ path, fullPage });
resolve(result);
} catch (error) {
reject(error);
}
};
page.on('console', onConsole);
timer = setTimeout(() => {
cleanup();
reject(new Error(`No matching console message within ${timeout} ms`));
}, timeout);
});
}
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const capture = screenshotWhenConsole(page, {
type: 'warn',
text: 'layout fallback',
path: 'layout-warning.png',
timeout: 15_000
});
await page.goto('https://example.com');
await capture;
} finally {
await browser.close();
}
In this helper, cleanup occurs before the asynchronous screenshot starts. That prevents a second matching event from starting another capture while the first one is still writing.
Choose the screenshot area and file format
| Requirement | Puppeteer option | Result |
|---|---|---|
| Visible viewport | page.screenshot({ path }) |
Captures the current viewport. |
| Entire document | fullPage: true |
Captures the full scrollable page. |
| Rectangular region | clip: { x, y, width, height } |
Captures the specified viewport coordinates. |
| One element | await elementHandle.screenshot({ path }) |
Scrolls the element into view and captures it. |
| Output type | File extension such as .png, .jpeg, or .webp |
Puppeteer infers the format from the path. |
For example, to capture the element that appears after the message:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Rank #3
const card = await page.waitForSelector('#result-card');
await card.screenshot({ path: 'result-card.webp' });
If you need a stable viewport, set it before navigation with page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 }). A screenshot reflects the page state at the moment the call runs; if the matching console message is logged before an animation or data render completes, wait for a selector or a short, justified delay before calling screenshot().
Triggering the message yourself
After a click
const capture = screenshotWhenConsole(page, {
type: 'log',
text: 'report ready',
path: 'report.png'
});
await page.click('#generate-report');
await capture;
After injected JavaScript
const capture = screenshotWhenConsole(page, {
type: 'info',
text: 'demo complete',
path: 'demo.png'
});
await page.evaluate(() => console.info('demo complete'));
await capture;
After navigation
Start the wait first, then navigate. If navigation can reject because the page intentionally closes or redirects, handle that error separately while still awaiting the screenshot promise.
Concurrency, repeated messages, and browser lifetime
Console events can arrive faster than screenshots can be encoded. Do not launch several screenshot operations at once unless you intentionally want independent files. A simple serialized queue handles repeated matches:
let captureQueue = Promise.resolve();
let index = 0;
page.on('console', msg => {
if (msg.type() !== 'error' || !msg.text().includes('target phrase')) return;
const file = `error-${++index}.png`;
captureQueue = captureQueue.then(() => page.screenshot({ path: file }));
});
await page.goto('https://example.com');
await captureQueue;
Keep the browser open until every queued screenshot resolves. Closing it in a finally block, as in the first example, prevents orphaned Chromium processes when navigation, a timeout, or file writing fails.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #4
Troubleshooting
The script times out
- Cause: The message was emitted before the listener was attached. Fix: create the wait promise before
goto(),click(), orevaluate(). - Cause: The type or text filter is too strict. Fix: temporarily log
msg.type()andmsg.text(), then adjust the filter. Check capitalization and dynamic text. - Cause: The page never reaches the code path. Fix: verify selectors, navigation outcomes, authentication, and any prerequisite network requests.
The screenshot is from the wrong state
- The console message may occur before the UI update. After the event, wait for a known selector with
await page.waitForSelector(), or wait for the relevant network/UI condition rather than relying on an arbitrary long delay. - If several matching messages occur, use a specific substring or regular expression and a one-shot guard so an earlier diagnostic does not win.
Several files are written unexpectedly
That is expected with page.on() when the page emits multiple matches. Use page.once() or a captured flag for one image, or serialize a deliberate queue when every event matters.
The file is empty or the browser closes early
Await the screenshot promise before calling browser.close(). Also check that the destination directory exists and that the process has write permission. Supplying a path with the intended extension makes the output format explicit.
The browser fails to launch
Confirm that Puppeteer is installed in the same project and that the Node.js process can launch its bundled browser. In restricted CI environments, inspect the launch error for sandbox or executable-path requirements and apply the environment’s documented browser policy rather than hiding the error.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If you only need a clean image after a page has settled, ScreenshotNeo provides a one-call website screenshot API. It accepts the cookie or consent banner like a visitor, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and bills only clean shots. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing status. Its MCP server also gives Claude, Cursor, and other MCP clients take_screenshot, get_page_info, and capture_pdf tools.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Use the API with cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
The same request in Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
And in 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}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
See the parameter reference and options in the ScreenshotNeo documentation. It supports full-page and element captures, device and viewport settings, retina scale, PDFs, custom CSS or JavaScript, clicks, waits, blocked resources, headers, cookies, user agents, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work, which can simplify a migration.
Best Value
- JavaScript Jquery
- Introduces core programming concepts in JavaScript and jQuery
- Uses clear descriptions, inspiring examples, and easy-to-follow diagrams
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account.
Practical checklist
- Attach the
consolelistener before the triggering action. - Filter by both
type()andtext()when unrelated messages are possible. - Use a timeout so a missing message fails explicitly.
- Choose viewport, full-page, clip, or element capture deliberately.
- Use the filename extension to select PNG, JPEG, or WebP.
- Guard or queue repeated matches to avoid races.
- Await all screenshot work before closing the browser.
FAQ
Can I capture after any console level?
Yes. Match the value returned by msg.type(), including error, warn, log, or info, and combine it with text matching when needed.
Does Puppeteer provide a dedicated “wait for console” method?
No special wait API is required. The page is an event emitter, so a Promise around the console event supplies the wait and lets your script apply its own timeout and matching rules.
Can the screenshot be returned instead of saved?
Yes. Calling page.screenshot() without path returns image data; supplying path writes the file. The examples use a path so the result is immediately available on disk.
Frequently Asked Questions
Can I capture after any console level?
Yes. Match the value returned by msg.type(), including error, warn, log, or info, and combine it with text matching when needed.
Does Puppeteer provide a dedicated “wait for console” method?
No special wait API is required. The page is an event emitter, so a Promise around the console event supplies the wait and lets your script apply its own timeout and matching rules.
Can the screenshot be returned instead of saved?
Yes. Calling page.screenshot() without path returns image data; supplying path writes the file.
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.

