How to Fix Firebase Functions Timeouts When Puppeteer Opens a Page
Find out whether Firebase, Chromium launch, page navigation, or a content wait is timing out—and fix the right layer with clear logs and bounded waits.

When Puppeteer opens a page from Firebase Functions and times out, first identify which clock expired. The function has an overall execution deadline; Puppeteer also has separate navigation and content-wait timeouts. Chromium startup can stall or fail before navigation even begins. Increasing a limit only helps when that particular limit is the cause.
Add timestamped logs around browser launch, page creation, navigation, any selector or application-condition wait, result processing, and response completion. Then use the error and last completed stage to choose a fix. A missing Chromium executable needs a packaging fix; a slow navigation needs a page-readiness decision; a function deadline needs a runtime configuration or workload change.
1. Identify which timeout expired
“Firebase Functions timeout Puppeteer” is not one error with one remedy. These failures can look similar if you only notice that the invocation ended without a result. Record the complete error and stack trace, and measure each operation in the deployed function.

| Layer | Typical clue | What to inspect |
|---|---|---|
| Firebase invocation | The function reaches its configured deadline or is terminated. | Trigger type, deployed runtime options, total elapsed time, and the last stage log. |
| Chromium startup | Launch stalls, connection fails, or Chromium cannot be found. | Browser executable, install and build logs, package versions, launch logs, and memory. |
| Navigation | Navigation timeout of 30000 ms exceeded or a similar navigation error. |
Time spent in page.goto(), target response, chosen waitUntil, and network behavior. |
| Page content wait | A selector or application condition times out after navigation. | Whether the content appears in production, whether the selector matches, and what the page returned. |
A Puppeteer timeout does not extend Firebase’s deadline. Likewise, giving the function more time does not install a missing browser or make a selector appear. Diagnose before changing either setting.
2. Log the slow stage in the deployed function
Use a small logging helper that records elapsed time and preserves the operation name. Include deployed context such as the function name, trigger type, Node.js version, and the exact Puppeteer and Chromium package versions. Avoid logging secrets, cookies, authorization headers, or private page contents.
const startedAt = Date.now();
function mark(stage, details = {}) {
console.log(JSON.stringify({
stage,
elapsedMs: Date.now() - startedAt,
...details,
}));
}
let browser;
try {
mark("launch:start");
browser = await puppeteer.launch(launchOptions);
mark("launch:done");
const page = await browser.newPage();
mark("navigation:start", { url: targetUrl });
const response = await page.goto(targetUrl, {
waitUntil: "domcontentloaded",
timeout: 30_000,
});
mark("navigation:done", {
status: response ? response.status() : null,
});
mark("content-wait:start");
await page.waitForSelector("main", { timeout: 10_000 });
mark("content-wait:done");
const html = await page.content();
mark("result:done", { bytes: Buffer.byteLength(html) });
return html;
} catch (error) {
mark("error", { message: error.message, stack: error.stack });
throw error;
} finally {
if (browser) {
await browser.close();
mark("browser:closed");
}
}
This is a diagnostic pattern, not a universal Firebase deployment recipe: launchOptions, function wrappers, and browser packaging depend on your runtime and package versions. Put logs before and after each awaited stage, and check the deployed function’s logs rather than relying only on the emulator. A log showing navigation started but not finished narrows the search; a missing launch completion points earlier in the chain.
3. Set Firebase’s function deadline for the actual trigger
Firebase’s function-level timeout covers the whole invocation. The documented maximum depends on the trigger: HTTP and callable functions can be configured up to 3600 seconds; scheduled and task queue functions up to 1800 seconds; other event-driven functions up to 540 seconds. Confirm your trigger and function generation before copying a value. [Firebase runtime options and timeout limits]
For the current Node.js API shape, configure runtime options in the function definition and redeploy:
exports.renderPage = onRequest({
timeoutSeconds: 120,
memory: "1GiB",
}, async (request, response) => {
// Validate the request, launch Puppeteer, open the page,
// collect the result, and send the response.
});
The 120-second value is illustrative, not a recommended default. Choose a deadline from measured execution time and normal variability, leaving room for the function to finish cleanup and send its response. Firebase documents code-side runtime options as the source of truth by default; they can override console or CLI settings. If the deployed value differs from the code, inspect the runtime configuration and redeploy. [Firebase: manage functions]
First-generation projects use a different API form, documented by Firebase as runWith({timeoutSeconds, memory}). Match the example to the generation and SDK already in the project. Do not assume a setting was applied because it appears in a console: compare it with the deployed configuration and code-side options.
Raising the Firebase deadline is appropriate when the full task legitimately needs more time and remains within the trigger’s limit. If the browser task routinely exceeds a practical HTTP response window, consider an asynchronous task or background workflow that returns a job identifier. Choose that design based on how the trigger and caller need to receive results; a longer synchronous deadline is not the only scaling option.
4. Fix Chromium installation and launch problems
“Puppeteer works locally but times out after deploying Firebase Functions” often means the deployed environment differs from the development machine. Having Puppeteer in package.json does not prove that its browser executable was installed, included, or discoverable in the deployed function.

Puppeteer’s troubleshooting guidance for Google Cloud Functions recommends declaring Puppeteer as a dependency and placing its browser cache under node_modules. Cloud Functions can cache node_modules between builds; when an install reuses that cache, the Puppeteer install step that fetches a browser may not run. Follow the current Puppeteer instructions for the project’s version, then inspect build logs and verify the expected browser path exists in the deployed package. [Puppeteer troubleshooting: Google Cloud Functions]
For projects using puppeteer-core with an alternate serverless Chromium package, follow that package’s instructions for the exact deployed release. Check its documented Puppeteer compatibility, executable-path flow, and launch options. For example, Sparticuz Chromium points users to its version-specific instructions and Puppeteer’s Chromium support information. Avoid copying flags or paths from an unrelated Lambda, Cloud Run, or older Firebase example. [Sparticuz Chromium for serverless platforms]
Log and compare the deployed Node.js, Puppeteer, and Chromium versions with local versions. When launch stalls, inspect launch duration, executable path, browser stderr where available, and memory pressure. A Could not find Chrome error is an installation or discovery problem; increasing navigation timeout cannot fix it.
5. Choose a bounded page-readiness condition
page.goto() has its own navigation timeout. After navigation, waitForSelector and other waits have their own time limits too. Select a readiness condition that matches what the function needs, and give each wait a deliberate finite bound. Puppeteer’s Page API documents navigation and wait behavior. [Puppeteer Page API]
domcontentloadedcan be enough when the task needs initial markup and does not rely on later rendering.loadwaits for the page’s load event, which can be useful when the requested result depends on load-complete resources.networkidlecan be a poor fit for pages with polling, analytics, or long-lived requests. A page may keep network activity open even after the content you need is ready.waitForSelectoris useful when a specific element signals that the application has rendered the needed content. Verify the selector against the deployed page and handle cases where it is absent.
When navigation is slow, inspect the response and page behavior before raising its timeout. When an element wait expires, check whether the site returned an error page, redirected, required authentication, or changed markup. If you need a specific application state, wait for that condition rather than waiting indefinitely for all network requests to stop. [Puppeteer Page API]
6. Give Chromium enough resources and always close it
Browser work can need more memory and CPU than ordinary request handling. Firebase lets you set function memory, and second-generation CPU defaults vary with allocated memory. Profile launch and navigation in the deployed environment; look for memory termination or resource pressure in logs. There is no single memory setting that fits every site. [Firebase runtime options]
Close the browser on success and error. A leaked browser can consume resources and make later work less reliable, especially when an invocation is reused or the workflow has multiple steps. Await browser.close() in a finally block, as in the logging example. Sparticuz Chromium also advises awaiting browser closure when a script returns an error. [Sparticuz Chromium guidance]
Also validate the requested URL and bound your work. Set a maximum for navigation and content waits, avoid retrying a failed page forever, and distinguish retryable target failures from configuration errors such as a missing executable. If you retry transient failures, account for the total invocation deadline so retries and cleanup still fit.
7. Troubleshooting common errors
| Error or symptom | Likely cause | Fix to try first |
|---|---|---|
| Function logs say the configured deadline was reached | The whole invocation exceeded its Firebase timeout, perhaps in launch, navigation, content wait, or cleanup. | Use stage timings; verify trigger and deployed timeout; raise the code-side value only within its documented limit, or reduce/move the work. |
Could not find Chrome or executable missing |
Browser installation, cached dependency build, or executable discovery issue. | Follow Puppeteer’s Cloud Functions cache guidance; inspect install logs and deployed browser path. |
Timed out while trying to connect to the browser |
Chromium failed to start or Puppeteer could not connect; versions, path, or resource headroom may be wrong. | Measure launch separately; verify the deployed executable and supported package versions; inspect available browser logs and memory. |
Navigation timeout of 30000 ms exceeded |
The page did not reach the chosen navigation condition before Puppeteer’s page-level deadline. | Check navigation timing and target response; choose an appropriate waitUntil; set a deliberate finite page timeout. |
waitForSelector times out |
The selector does not match, content is delayed, or the page is an error, redirect, or authentication page. | Inspect status and returned page state; verify the selector in production; wait for the actual condition with a bounded timeout. |
| Local run succeeds, deployed function fails | Different Node/browser versions, runtime options, dependency cache, or deployed files. | Compare versions, build logs, runtime configuration, and browser presence in the deployment artifact. |
These clues are directions for diagnosis, not guarantees about a particular stack trace. The title alone does not reveal function generation, trigger, SDK, Puppeteer version, Chromium distribution, or target site behavior. Gather those details before making a version-specific upgrade or copying launch flags.
8. A practical debugging checklist
- Copy the full error message and stack trace from deployed logs.
- Record function name, trigger type, generation, Node.js version, and deployed runtime options.
- Log elapsed time before and after
puppeteer.launch(),newPage(),page.goto(), each content wait, result processing, and response completion. - If launch stalls or the executable is missing, check the build cache, browser path, package versions, and deployed files.
- If navigation expires, inspect the target response and choose the least restrictive readiness condition that still returns the content you need.
- If a later wait expires, confirm the expected content and selector exist in the deployed page.
- Check memory and CPU behavior, then set a measured Firebase deadline within the limit for the trigger.
- Ensure cleanup runs and the caller receives a useful error or result.
Or skip the browser setup
If your function only needs a website screenshot, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the page verdict and billing status. AI agents can use its MCP server tools, including take_screenshot, get_page_info, and capture_pdf. Every feature is on every plan: 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. See the ScreenshotNeo website and API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Make the same GET request from Python or Node.js:
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}`);
Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots; and 1,000 screenshots a month are free with no card, with paid plans starting at $5 for 3,000. Create a free ScreenshotNeo account.
Performance, reliability, and cost notes
Track launch and page time separately: optimization depends on which stage dominates. Reusing a browser might reduce repeated startup work in some architectures, but changes cleanup and isolation requirements; measure before adopting it. A slow or unavailable third-party page is different from a missing browser, and retries can add latency and consume the remaining invocation time. Set finite bounds and make retries deliberate.
Firebase’s runtime settings and trigger limit define how long an invocation may run. More memory can change CPU availability in second-generation functions, but it also affects resource allocation; profile the actual workload instead of treating a timeout increase as a performance fix. For repeated or long-running captures, an asynchronous workflow can make caller latency more predictable, provided its job state and result delivery suit the application.
Account for the cost of the function’s runtime and memory configuration when choosing a deadline. A longer ceiling does not mean every invocation will use it, but slow pages and retries can increase execution duration. Likewise, allocating more memory is not a substitute for fixing a browser-installation failure or a wait condition that never becomes true.
FAQ
Does Firebase’s timeout setting change Puppeteer’s navigation timeout?
No. Firebase limits the whole invocation; Puppeteer navigation and wait APIs have separate page-level timeouts. Configure and diagnose each layer independently.
Should I always use networkidle?
No. Polling, analytics, or long-lived requests can keep network activity open. Use the readiness condition that matches the content your task needs.
Why does increasing timeoutSeconds not fix a missing browser?
The function deadline only changes how long the invocation may run. Chromium must still be installed and discoverable in the deployed environment.
What details are needed for a version-specific fix?
Share the trigger type, Functions generation, Node.js and SDK versions, Puppeteer and Chromium package versions, complete stack trace, and the last stage that completed in deployed logs.


