lissy93/web-check · warning · Error
No body element found on the page
Error message
No body element found on the page
What it means
During a Puppeteer screenshot run (dark-mode emulation, domcontentloaded wait), the page is evaluated and document.querySelector('body') returns null, so the in-page script throws. HTML documents always have a body node synthesised by the parser, so this only fires when the navigated document is not HTML (e.g. XML, plain text in some contexts) or the page frame is in an unexpected state after navigation.
Source
Thrown at api/screenshot.js:62
// Fallback to puppeteer when the direct Chromium binary call fails
const puppeteerScreenshot = async (targetUrl) => {
let browser = null;
try {
browser = await puppeteer.launch({
args: [...chromium.args, '--no-sandbox'],
defaultViewport: { width: 800, height: 600 },
executablePath: process.env.CHROME_PATH || (await chromium.executablePath()),
headless: true,
acceptInsecureCerts: true,
ignoreDefaultArgs: ['--disable-extensions'],
});
const page = await browser.newPage();
await page.emulateMediaFeatures([{ name: 'prefers-color-scheme', value: 'dark' }]);
page.setDefaultNavigationTimeout(8000);
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
await page.evaluate(() => {
if (!document.querySelector('body')) {
throw new Error('No body element found on the page');
}
});
const buffer = await page.screenshot();
return buffer.toString('base64');
} finally {
if (browser) await browser.close().catch(() => {});
}
};
const screenshotHandler = async (targetUrl) => {
if (!targetUrl) throw new Error('URL is missing from queryStringParameters');
try {
new URL(targetUrl);
} catch {
throw new Error('URL provided is invalid');
}
log.debug(`request received: ${targetUrl}`);View on GitHub (pinned to af1a97759f)
Solutions
- Verify the target actually serves text/html (curl -I <url>) and fix the URL
- Have the caller check Content-Type before requesting a screenshot
- If you control this code, treat a missing body as a skip/soft-failure rather than a hard error, as the handler already does for Chromium-missing errors
Example fix
// before
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.screenshot(); // throws 'No body element found on the page' for XML docs
// after
const res = await page.goto(url, { waitUntil: 'domcontentloaded' });
if (!(res.headers()['content-type'] || '').includes('html')) {
return { skipped: `non-HTML content-type: ${res.headers()['content-type']}` };
}
await page.screenshot(); Defensive patterns
Strategy: fallback
Validate before calling
const res = await fetch(target, { method: 'HEAD', redirect: 'follow' });
const ct = res.headers.get('content-type') || '';
if (!ct.includes('html')) skipScreenshot('non-HTML target'); Type guard
null
Try / catch
try { return { image: await screenshot(url) }; }
catch (e) {
if (e.message === 'No body element found on the page') return { skipped: 'target is not an HTML page' };
throw e;
} Prevention
- Content-Type check before handing URLs to a screenshot renderer
- Treat renderer anomalies as skips, mirroring the Chromium-not-found handling already present
- Log the final response URL and content-type when screenshots fail to triage redirects
When it happens
Trigger: Screenshotting a URL that serves application/xml, an RSS/Atom feed, or a non-HTML resource; a redirect chain landing on a non-HTML document; about:blank-ish or prematurely aborted loads where DOMContentloaded fired on a skeleton document.
Common situations: Feeds and API endpoints passed to a screenshot API, XML sitemap URLs, endpoints that sniff content-type differently for headless Chromium's User-Agent.
Related errors
AI-assisted analysis of lissy93/web-check@af1a97759f (2026-08-27).
Data as JSON: /api/errors/23b887f0a547d4e7.
Report an issue: GitHub.