ScreenshottyScreenshotty
FeaturesDocumentationPricingBlog
ScreenshottyScreenshotty

Most cost-effective Screenshot API

Product

  • Features
  • Pricing
  • Documentation
  • Blog
  • Compare
  • Alternatives

Free Tools

  • Website Screenshot Tool
  • Website Responsivity Check
  • Website Text Extractor
  • Website to PDF Converter
  • Website Markdown Converter
  • All Free Tools

Developers

  • Python Screenshot API
  • Node.js Screenshot API
  • PHP Screenshot API
  • Full Page Screenshots
  • URL to PDF API
  • API Parameters
  • MCP Server

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Screenshotty. All rights reserved.

ScreenshottyScreenshotty
FeaturesDocumentationPricingBlog
ScreenshottyScreenshotty

Most cost-effective Screenshot API

Product

  • Features
  • Pricing
  • Documentation
  • Blog
  • Compare
  • Alternatives

Free Tools

  • Website Screenshot Tool
  • Website Responsivity Check
  • Website Text Extractor
  • Website to PDF Converter
  • Website Markdown Converter
  • All Free Tools

Developers

  • Python Screenshot API
  • Node.js Screenshot API
  • PHP Screenshot API
  • Full Page Screenshots
  • URL to PDF API
  • API Parameters
  • MCP Server

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Screenshotty. All rights reserved.

ScreenshottyScreenshotty
FeaturesDocumentationPricingBlog
Puppeteer: "Execution context was destroyed" — Causes and Fixes
Troubleshooting

Puppeteer: "Execution context was destroyed" — Causes and Fixes

Jun 10, 2026
•
6 min read
Sarah Chen
Sarah Chen
Developer Advocate

If you run Puppeteer in production long enough, you will eventually hit this error:


Error: Execution context was destroyed, most likely because of a navigation.


It is one of the most common Puppeteer failures, and the message is accurate: your script was evaluating something inside a page whose JavaScript context disappeared mid-flight, almost always because the page navigated.


Why it happens


Every page in Puppeteer has an execution context — the JavaScript world your `page.evaluate()` calls run in. When the page navigates (a redirect, a form submit, a client-side route change that triggers a full reload, or a meta refresh), Chrome destroys that context and creates a new one. Any `evaluate`, `$eval`, or `waitForFunction` still running against the old context throws this error.


The classic triggers:


  • The page redirects after load (marketing pages and login flows love this)
  • Your script clicks something that navigates, then immediately evaluates
  • A single-page app performs a hard reload you didn't expect
  • An ad or analytics script forces a late navigation

  • Fix 1: Wait for navigation explicitly


    If an action you perform causes navigation, wrap it with `waitForNavigation` — and start waiting *before* the click:


    await Promise.all([

    page.waitForNavigation({ waitUntil: "networkidle0" }),

    page.click("a.next-page"),

    ]);


    const title = await page.evaluate(() => document.title);


    The `Promise.all` pattern matters. If you click first and call `waitForNavigation` after, the navigation can finish before the waiter attaches, and you hang or race.


    Fix 2: Settle redirects before evaluating


    For pages that redirect on load, wait until the network goes quiet before touching the page:


    await page.goto("https://example.com", { waitUntil: "networkidle0" });

    await page.waitForSelector("main");

    const html = await page.content();


    `waitForSelector` on an element you know exists post-redirect is more reliable than a fixed sleep.


    Fix 3: Retry around the race


    Sometimes you cannot predict the navigation (third-party scripts, A/B tests). A narrow retry is pragmatic:


    async function evaluateWithRetry(page, fn, retries = 3) {

    for (let attempt = 1; attempt <= retries; attempt++) {

    try {

    return await page.evaluate(fn);

    } catch (error) {

    const destroyed = error.message.includes("Execution context was destroyed");

    if (!destroyed || attempt === retries) throw error;

    await page.waitForNetworkIdle({ idleTime: 500 });

    }

    }

    }


    Fix 4: Stop racing the page entirely


    If the goal is a screenshot or a PDF, you are using a manual browser-automation tool for a solved problem. A screenshot API waits for the page to settle (redirects included), renders it in managed Chrome, and returns the image — no execution contexts to race:


    curl "https://api.screenshotty.link/api/v1/screenshot?url=https://example.com&ready_event=networkidle&full_page=true" \

    -H "X-Api-Key: YOUR_API_KEY" \

    --output screenshot.png


    [Screenshotty](/) handles redirects, lazy loading, cookie banners, and ads server-side, with a free tier of 100 screenshots per month. The [full-page screenshot API](/full-page-screenshot-api) and [Node.js guide](/screenshot-api/nodejs) cover the common cases.


    Summary


  • The error means your `evaluate` ran against a context killed by navigation.
  • Wrap navigating actions in `Promise.all` with `waitForNavigation`.
  • Settle redirects with `waitUntil: "networkidle0"` plus `waitForSelector`.
  • Retry narrowly when the navigation is outside your control.
  • For screenshots and PDFs specifically, an API removes the race condition class entirely.

  • Want to learn more?

    Explore our documentation and start building with Screenshotty

    ScreenshottyScreenshotty

    Most cost-effective Screenshot API

    Product

    • Features
    • Pricing
    • Documentation
    • Blog
    • Compare
    • Alternatives

    Free Tools

    • Website Screenshot Tool
    • Website Responsivity Check
    • Website Text Extractor
    • Website to PDF Converter
    • Website Markdown Converter
    • All Free Tools

    Developers

    • Python Screenshot API
    • Node.js Screenshot API
    • PHP Screenshot API
    • Full Page Screenshots
    • URL to PDF API
    • API Parameters
    • MCP Server

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Screenshotty. All rights reserved.