Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“Uncaught [object Object]” is a symptom, not a diagnosis. It usually means JavaScript threw a non-Error value, or that an exception was converted to text in a way that hid its useful fields. Capture the original exception and the operation that triggered it before changing Chrome flags or versions. Then determine whether the failure is in page code, navigation, a test assertion, or screenshot capture.

What the message actually means

JavaScript can throw any value, including a plain object: throw { code: 'PAYMENT_FAILED' }. Error reporting normally expects an Error with a name, message, and stack. When a thrown object is converted to a string, its useful fields may disappear and the display can collapse to [object Object]. Chromium’s exception-handler tests also demonstrate a thrown object whose own toString() throws, producing an Uncaught [object Object]-style message.

That behavior does not prove that Chrome itself is broken. The same text can be emitted by application code, a test framework, or a screenshot provider. Treat it as a clue that the original thrown value and call context were lost.

1. Capture the original exception before it is converted

Install the page-error listener before navigation or the action that fails. In Playwright, the pageerror event is emitted when an uncaught exception happens within the page. Log the object itself, then inspect its standard fields and safe enumerable properties. Avoid relying on string interpolation alone.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const { chromium } = require('playwright');
const util = require('node:util');

function describeException(value) {
  const result = { type: typeof value };
  if (value && typeof value === 'object') {
    for (const field of ['name', 'message', 'stack']) {
      try {
        if (value[field] !== undefined) result[field] = value[field];
      } catch (readError) {
        result[field] = '[field could not be read]';
      }
    }
    result.properties = {};
    for (const key of Object.keys(value)) {
      try {
        result.properties[key] = value[key];
      } catch (readError) {
        result.properties[key] = '[property could not be read]';
      }
    }
  }
  return result;
}

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  page.on('pageerror', exception => {
    console.error('Raw page exception:', util.inspect(exception, { depth: 6 }));
    console.error('Structured exception:', describeException(exception));
  });

  page.on('console', message => {
    if (message.type() === 'error') console.error('Browser console:', message.text());
  });

  try {
    await page.goto('https://example.com', { waitUntil: 'networkidle' });
    await page.screenshot({ path: 'debug.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Do not blindly call JSON.stringify(exception): exception objects can contain circular references, getters, or values whose conversion throws. If you must serialize logs for CI, copy only the fields you can read safely and include the URL, event name, and timestamp.

2. Identify the operation that failed

Record the first failing action, not just the final error line. A page exception during navigation requires a different fix from a screenshot stream that is incomplete after the page loaded.

Where it fails Useful evidence What to investigate
Page load or script execution pageerror, browser-console errors, URL and navigation timing Application code, rejected promises, scripts that throw plain objects, and failed resource dependencies
User interaction Selector, action, page state, and the last successful step Event handlers, validation code, overlays, and race conditions
Assertion Expected value, actual value, and stack from the test runner Test logic rather than Chrome rendering
Screenshot capture Screenshot warning, output size, parser error, and whether navigation succeeded Framework screenshot code, browser/runtime compatibility, and an incomplete image stream

If the screenshot is the only failing operation, preserve every screenshot-related warning. A parser error can be the downstream result of an interrupted or empty PNG response, not the original exception.

3. Record the complete runtime context

Write down the exact environment for a failing run. The message has appeared in tightly scoped combinations of framework, Node.js, Chrome, and operating-system versions, so a version number without the others is not enough.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Automation framework and exact version.
  • Node.js version.
  • Chrome or Chromium version and executable path.
  • Operating system and architecture.
  • Headless or headed mode.
  • Launch flags, sandbox settings, container image, and display server configuration.
  • URL, test name, action, viewport, device emulation, and screenshot options.
  • Whether the same run works locally, in CI, or only in one browser channel.

Save this information with the failure so a later dependency update can be compared against a known baseline.

4. Reduce the reproduction

  1. Keep the failing URL and one action. Remove unrelated fixtures, plugins, reporters, and test hooks.
  2. Capture page errors immediately. Register the listener before goto, login, clicking, or screenshot code.
  3. Separate page loading from capture. First load the page and wait for the required state; then take the screenshot in a separate step.
  4. Compare headed and headless runs. Use the same browser binary, profile, viewport, URL, and flags. A headed success narrows the problem to the headless/display path; it does not prove a Chrome defect.
  5. Change one component at a time. Compare one framework version, Node.js version, Chrome channel, or operating system at a time. Record which change alters the exception.
  6. Retain artifacts. Keep console output, page-error details, screenshots, traces, and the exact command line from both the passing and failing runs.

5. Fix the layer that throws

When application code throws a plain object

Change the application to throw an Error with a meaningful message and stack, while preserving structured information as properties.

async function chargeCustomer(payload) {
  const response = await fetch('/api/charge', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(payload)
  });

  if (!response.ok) {
    const details = await response.json().catch(() => ({}));
    const error = new Error('Charge request was rejected');
    error.code = 'CHARGE_REJECTED';
    error.status = response.status;
    error.details = details;
    throw error;
  }

  return response.json();
}

At the test boundary, log name, message, stack, and selected data fields. Do not replace the original error with a new string; that destroys the stack needed to locate the throw site.

When only an automation operation fails

If page code produces no exception and the failure occurs during screenshot capture, inspect the framework’s screenshot implementation and its browser compatibility for the recorded versions. Keep the reproduction minimal and test the same page in headed mode, but do not assume that upgrading, downgrading, or adding a launch flag is a universal fix. The available evidence does not establish one version change that resolves every occurrence.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The known TestCafe screenshot report

A TestCafe report provides a concrete, historical example rather than a general diagnosis. It described screenshots failing in headless Chrome with Uncaught object "[object Object]" was thrown. Throw Error instead. The report used TestCafe 2.1.0, Node.js 18.12.1, Chrome 108.0.5359.94, and macOS 10.15.7; its reproduction steps included Node.js 17, 18, or 19.

For TestCafe versions below 2.0.1, the reported symptom was instead a warning that the screenshot could not be taken together with a PNG parser error, Unexpected end of input. That is consistent with an incomplete image stream, but it does not establish a current defect on every platform or a single current fix. Use the report to justify collecting precise versions and preserving parser output, not to label every headless Chrome error the same way.

Compare the right variables

When one setup passes and another fails, compare these axes independently:

Axis Controlled comparison Interpretation
Code versus runner Run the page manually or with a minimal script, then run the full test Separates application exceptions from test hooks and providers
Operation Load, interact, assert, and capture in separate runs Identifies the first operation that emits the failure
Display mode Headless and headed with all other settings unchanged Shows whether display mode is a contributing condition
Versions Pin framework, Node.js, and Chrome; alter only one Finds compatibility changes without guessing
Operating system Repeat in the same container or OS image Exposes sandbox, graphics, and packaging differences

Common symptoms and targeted fixes

The log contains only [object Object]

Install the page-error listener earlier, log the raw value with an object inspector, and copy safe fields instead of interpolating the value into a string. If the object has a custom toString, avoid invoking it while diagnosing.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The error appears during navigation

Capture the URL, response status, console errors, and page-error event. Reduce the page to the smallest route that still throws, then fix the script or rejected promise that produced the non-Error value.

Navigation succeeds but screenshots fail

Save the screenshot warning and parser output, verify that the page is fully loaded, and test a minimal screenshot with no extra hooks. Compare headed and headless mode and check the pinned framework, Node.js, Chrome, and OS versions.

Only CI fails

Compare executable paths, container images, sandbox flags, viewport, fonts, environment variables, and network access. Preserve the full launch command and artifacts; a CI-only failure is not evidence that the page is universally broken.

Changing versions made the message disappear

Keep the passing combination documented, then bisect one dependency at a time. A disappearing message shows that compatibility changed, but it does not identify which layer was at fault unless the reduced reproduction isolates it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Reliability and performance practices

  • Register diagnostics before any asynchronous navigation or interaction.
  • Wait for a defined page condition rather than an arbitrary long delay, and record timeout values.
  • Use one browser context per independent reproduction to avoid state leaking between tests.
  • Store failed screenshots and logs with the same run identifier.
  • Pin browser and framework versions in CI, and update them in controlled batches.
  • Do not retry blindly: a retry can hide a deterministic page exception or produce another incomplete screenshot. Retry only after recording the first failure.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a reliable website image rather than debugging a local browser, ScreenshotNeo provides a GET request that returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response reports the result in X-Page-Verdict and X-Billed headers.

Use the API documentation at https://screenshotneo.com/docs/ for request options. This one-call example captures a page without installing Chrome or managing a display server:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.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://stripe.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://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
require('node:fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));

For automation workflows, ScreenshotNeo also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Options include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper settings and page ranges, custom CSS and JavaScript, click-before-capture, selector or network-idle waits, request and resource blocking, custom headers, cookies, user agents and authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for 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 migration.

Plan Included screenshots Price
Free 1,000 per month $0, no card
Starter 3,000 $5
Growth 15,000 $15
Pro 60,000 $39
Scale 250,000 $99
Business 1,000,000 $249

Every feature is included on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

FAQ

Should I redact exception properties in CI logs?

Yes. Keep the fields needed to reproduce the failure, but remove tokens, cookies, authorization headers, and customer data before uploading artifacts.

Can a successful headed run certify that headless Chrome is defective?

No. It only identifies display mode as a useful comparison axis. The page, runner, browser build, operating system, or launch configuration may still be responsible.

Is the historical TestCafe report a current universal Chrome bug?

No. It documents a specific 2022 environment and earlier-version behavior. Current diagnosis still requires the captured exception, failing operation, and complete runtime context.

Frequently Asked Questions

What should I preserve when filing a bug report?

Include the raw exception fields, first failing operation, framework and Node.js versions, Chrome version, operating system, headless/headed mode, launch flags, and a minimal reproduction.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why is “Unexpected end of input” useful after a screenshot failure?

It indicates that the image parser received an incomplete PNG stream, which helps distinguish capture-output failure from a normal page assertion.

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.