Frozen/discarded background tabs can make page enumeration fail with Network.enable timeout

View original issue on GitHub  ·  Variant 1

Handling Network.enable Timeouts in Chrome DevTools MCP

The chrome-devtools-mcp library can encounter issues when working with Chrome profiles that contain background tabs that have been frozen or discarded by Chrome to save memory. This problem manifests as a timeout error during page enumeration, specifically when Puppeteer attempts to execute Network.enable while constructing Page sessions.

This article will guide you through understanding the problem, identifying the root cause, and implementing a solution to make your chrome-devtools-mcp more robust when dealing with long-lived browsing profiles.

Understanding the Problem

When Chrome freezes or discards background tabs, these tabs become temporarily unresponsive to CDP commands. While browser-level CDP commands might still function correctly, page-session commands like Network.enable can time out. This timeout disrupts the page enumeration process, causing chrome-devtools-mcp to fail during initialization.

A key observation is that the affected targets often recover immediately after Target.activateTarget is called, suggesting that the issue isn't a broken browser connection but rather a suspended or discarded page target.

Root Cause Analysis

The root cause lies in how Chrome manages memory by freezing or discarding inactive tabs. When chrome-devtools-mcp attempts to connect to these tabs, it encounters a delay or failure in enabling the network, leading to the timeout. Although a previous fix was attempted in Chrome M146, the issue persists, indicating that there are still scenarios where Chrome can discard tabs in a way that triggers this problem.

Solution: Skipping Unresponsive Targets

A practical solution is to modify the chrome-devtools-mcp initialization flow to skip targets that are unresponsive during page-session initialization. This approach involves implementing a timeout mechanism for Network.enable and handling the timeout gracefully by skipping the problematic target.

Here's a code snippet demonstrating how to test the connection to a target using puppeteer-core:


const puppeteer = require('puppeteer-core');

async function testTargetConnection(target) {
  try {
    const session = await target.createCDPSession();
    await Promise.race([
      session.send('Network.enable'),
      new Promise((_, reject) => setTimeout(() => reject(new Error('Network.enable timed out')), 4000))
    ]);
    await session.detach();
    return true; // Connection successful
  } catch (error) {
    console.error(`Failed to connect to target: ${error.message}`);
    return false; // Connection failed
  }
}

You can integrate this function into your chrome-devtools-mcp workflow to filter out unresponsive targets before proceeding with page enumeration. Here's how you might use it to filter targets:


async function getActivePages(browser) {
  const targets = browser.targets().filter(t => t.type() === 'page');
  const activePages = [];

  for (const target of targets) {
    if (await testTargetConnection(target)) {
      const page = await target.page();
      if (page) {
          activePages.push(page);
      }
    } else {
      console.warn(`Skipping unresponsive target: ${target.url()}`);
    }
  }

  return activePages;
}

In this example, getActivePages iterates through the targets, tests the connection using testTargetConnection, and only adds the page to the activePages array if the connection is successful. This prevents the entire enumeration process from failing due to a single unresponsive target.

Practical Tips and Considerations

By implementing these strategies, you can enhance the robustness of your chrome-devtools-mcp applications and ensure they function reliably even when dealing with long-lived browsing profiles containing frozen or discarded tabs.