Short answer: Playwright’s screenshot preparation waits for the browser’s document.fonts.ready promise. The call-log line “waiting for fonts to load…” tells you which stage was active when the timeout fired, but it does not prove that a particular font request caused the failure. A synchronous page-event callback, a blocked resource, CSP, JavaScript that never settles, or a version-specific issue can leave the operation unable to finish.
What Playwright is waiting for
Before writing an image, Playwright prepares the page and, in current main-branch source, logs “waiting for fonts to load…” and awaits document.fonts.ready. It then logs “fonts loaded.” The exact implementation is version-sensitive, so compare this behavior with the Playwright release installed in your project.
FontFaceSet.ready is broader than “the last font request returned.” The browser resolves it after fonts needed by the document have loaded, layout work has completed, and no further font loads are required. A delayed or blocked web-font request can therefore matter, but page code that prevents the browser from progressing can produce the same visible wait.
The environment variable PW_TEST_SCREENSHOT_NO_FONTS_READY is checked by the inspected screenshotter. When set, the explicit document.fonts.ready wait is skipped. That is a diagnostic and a trade-off, not a universal fix: capture can occur before the intended web fonts are applied, and older or different Playwright builds may not recognize the variable.
Recommended Free Tools
#1 Best Overall
Why the log is not proof of a font failure
Playwright issue #35972 (reported against Playwright 1.49.0 and opened May 16, 2025) illustrates the distinction. The first report associated the timeout with a CSP-related font error. In the follow-up discussion, the maintainer identified a synchronous console-message handler that was deadlocking the screenshot call; moving the screenshot outside that handler resolved the described reproduction. The lesson is to treat the log as the active stage, not as a root-cause diagnosis.
Another report, issue #35200 (opened March 14, 2025), described a short timeout at the font-wait stage. Such individual reports establish possible failure modes, not a prevalence rate or a guaranteed fix for every project. A screenshot can also continue timing out after the font wait is bypassed, which means the first displayed stage may only be the first symptom.
First response: move capture out of synchronous page callbacks
Check the calling context before changing fonts or increasing timeouts. Do not call page.screenshot() directly inside a synchronous page event handler such as a console, request, response, or dialog callback when that handler is still part of the event being processed. Store the event data, release the callback, and capture afterward.
Problematic pattern (Node.js)
page.on('console', message => {
// The callback is still being processed while this awaits capture.
page.screenshot({ path: 'shot.png' });
});
Safer sequencing
const consoleMessages = [];
page.on('console', message => {
consoleMessages.push({ type: message.type(), text: message.text() });
});
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'shot.png', timeout: 30_000 });
console.log(consoleMessages);
If you need to react to one event, resolve a promise from the handler and perform the screenshot after the promise has settled and control has returned to the test body. Avoid a synchronous handler that waits on a Playwright operation which itself needs the page event loop to advance.
A repeatable diagnosis
- Record versions and context. Note the Playwright package version, language binding, browser channel, operating system, screenshot options, timeout, and whether the call is inside an event callback. The inspected implementation is the moving main branch, not a promise that every released version behaves identically.
- Reproduce with a minimal page. Try a static page with no custom listeners, service workers, third-party scripts, or authentication. If that succeeds, add your listeners and page features one at a time. This separates screenshot sequencing from site behavior.
- Collect page diagnostics. Log browser console messages, page errors, failed requests, and CSP violations. Filter for font URLs, but also inspect scripts, data requests, and resources that remain pending. A CSP message is a clue; it does not establish that fonts are the sole cause.
- Inspect font state in the page. Before capture, evaluate the browser state and measure how long it takes to settle:
const state = await page.evaluate(async () => {
const before = document.fonts.status;
const start = performance.now();
await document.fonts.ready;
return {
before,
after: document.fonts.status,
elapsedMs: Math.round(performance.now() - start),
fonts: [...document.fonts].map(font => ({
family: font.family,
status: font.status,
weight: font.weight,
style: font.style
}))
};
});
console.log(state);
This does not replace Playwright’s own preparation, but it tells you whether the page’s font set is actually settling or whether another operation is stalled.
Rank #2
- Test callback sequencing. Temporarily remove console and network handlers, especially handlers that perform synchronous work or await Playwright commands. If the timeout disappears, move capture outside the handler and restore listeners incrementally.
- Compare the installed release. Read the screenshot API documentation for your language binding and inspect the release-specific behavior. Do not assume a variable or option present in current source exists in your pinned version.
Choosing between preserving the wait and bypassing it
| Approach | Use when | Benefits | Costs and risks |
|---|---|---|---|
Diagnose and preserve document.fonts.ready |
You need production-quality typography or a deterministic visual test. | Captures after the browser says font loading and related layout work have settled; exposes real page and sequencing problems. | Blocked resources, deadlocks, or pages that continually trigger font work can still time out. |
Set PW_TEST_SCREENSHOT_NO_FONTS_READY=1 |
You need a failure screenshot or a quick diagnostic and can accept fallback fonts. | Removes the explicit screenshotter wait in versions that support the variable. | The image may be taken before web fonts are ready; it may not solve a JavaScript, callback, navigation, or resource hang. |
Using the bypass for a single diagnostic run
PW_TEST_SCREENSHOT_NO_FONTS_READY=1 npx playwright test
For Windows PowerShell, use $env:PW_TEST_SCREENSHOT_NO_FONTS_READY = "1" before running the test. In CI, set it only for the job or test that needs the diagnostic image. Check that your installed Playwright build supports the variable, and compare the resulting image with a normal run before adopting it for visual assertions.
Timeout settings: what they can and cannot fix
Screenshot APIs expose timeout controls through the Page API and language binding. Set a timeout that reflects the slowest legitimate navigation and font delivery in your environment, for example:
await page.screenshot({
path: 'page.png',
timeout: 30_000,
fullPage: true
});
A longer timeout helps when a font server is merely slow. It does not break a deadlock, repair a CSP policy, or make a page that continually starts new work become idle. Keep navigation and screenshot timeouts conceptually separate: diagnose navigation failures first, then determine whether screenshot preparation is the remaining wait.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Do not hide a permanently pending font
- Verify that the font URL is reachable from the browser’s network context, not only from your host machine.
- Check certificate, proxy, authentication, CORS, and CSP settings.
- Confirm that the server returns a valid font MIME type and completes the response.
- Check whether JavaScript repeatedly changes classes, styles, or content and triggers new font/layout work.
- Use a locally hosted test font or a minimal page to distinguish the site from the Playwright call sequence.
Common symptoms and targeted fixes
The call log stops at “waiting for fonts to load” immediately
Suspect call sequencing first, especially a screenshot invoked from a synchronous console or page-event callback. Remove the handler, reproduce, then move capture into the test flow after the callback returns.
Browser console reports a CSP font violation
Confirm the violation and the affected URL, then fix the site’s font-src policy or serve an allowed font. If the page intentionally uses a fallback, test whether document.fonts.ready settles in a minimal reproduction. Do not conclude that CSP alone explains the timeout when the screenshot is also called from an event handler.
Rank #3
The bypass still times out
This indicates that another stage is blocked. Inspect navigation, JavaScript execution, requests, page errors, and callback handlers. The issue #35972 follow-up records that skipping the font wait did not, by itself, resolve the reporter’s complete timeout.
The screenshot finishes but text uses fallback fonts
That is the expected fidelity risk of bypassing the wait. Remove the environment variable for visual assertions, or explicitly wait for document.fonts.ready after fixing the underlying resource or sequencing issue.
Free tools Windows power users keep installed
One-click scans. No signup required.
Only full-page screenshots fail
Full-page capture can expose additional lazy content and layout changes. Compare a viewport screenshot with fullPage: false, then inspect lazy-loaded sections, infinite scroll code, and elements whose styles change as the page height is measured. A font-stage message can be incidental to that broader layout work.
Increasing the timeout changes nothing
Assume deadlock or perpetual work rather than slowness. Capture a trace or diagnostic log, remove synchronous listeners, and test a minimal page before increasing limits again.
Reliable patterns for CI and visual tests
- Pin Playwright and browser versions in CI, and record them with failed artifacts.
- Use a controlled viewport, locale, timezone, and network policy so font selection is repeatable.
- Prefer self-hosted or dependable font delivery for visual baselines; third-party font availability can vary by run.
- Keep event handlers observational. Queue data from callbacks and perform screenshots in the main test sequence.
- Save console, page-error, request-failure, and CSP diagnostics with the image when a capture fails.
- Use the no-font-wait variable only for triage or explicitly fallback-font workflows, not silently in a typography-sensitive baseline.
Or skip the browser setup
If you only need a clean image or PDF of a URL, ScreenshotNeo provides a single HTTP request instead of requiring Playwright browser setup. Its screenshot preparation accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for parameters. This one-call example targets Stripe; replace the URL with yours:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also offers an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools. Its 63 options include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper and margin controls, custom CSS and JavaScript, pre-capture clicks, selector hiding, waits for a selector, delay, or network idle, request and resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for 100 URLs per call, a usage API, and an OpenAPI specification. Common parameter names used by other screenshot APIs are accepted to ease migration.
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free. Every feature is included on every plan. Sign up for the free ScreenshotNeo plan to try it without a card.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.FAQ
Does the message mean my font file is broken?
No. It identifies the screenshotter’s current wait stage. A broken or blocked font is one possibility; a synchronous callback deadlock or other page activity can produce the same message.
Is the environment variable a permanent solution?
Only when early capture and fallback typography are acceptable. Verify support in your installed version and do not use it to mask failures in a font-sensitive visual test.
Which timeout should I increase?
Use the screenshot timeout exposed by your binding, but first determine whether the page is slow or deadlocked. More time helps slow delivery, not a callback that prevents progress.
Why does a minimal reproduction pass while my application fails?
Your application may add event listeners, CSP restrictions, third-party resources, lazy content, or JavaScript that keeps changing layout. Reintroduce those pieces incrementally to locate the first change that recreates the timeout.
Frequently Asked Questions
Can I wait for fonts manually before calling screenshot?
Yes. Evaluating await document.fonts.ready can make page state observable, but it does not remove deadlocks or replace the screenshotter’s own preparation.
Does this issue affect every browser engine equally?
Not necessarily. Font loading, network behavior, and Playwright implementation details vary by browser engine, release, and language binding; record those variables when comparing runs.
Are timeout durations in Playwright issues production benchmarks?
No. Durations in individual issue reports are examples from those reproductions, not prevalence or performance statistics.
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.




