Debugging a Popup Before It Closes

Debug an extension popup that disappears whenever you click into DevTools: Inspect popup, opening popup.html in a tab, breaking on startup, logging early errors, preserving the popup during debugging, and automated popup tests.

Published October 2, 2026 Updated October 2, 2026 7 min read
Table of Contents

The popup shows a blank white box for a fraction of a second and closes. Or it opens fine, but as soon as you click into DevTools to set a breakpoint, the popup closes and takes its DevTools window with it. Popups close on blur by design, which makes them the most awkward extension context to debug: errors during startup happen before you can open DevTools, and interactive debugging fights the close-on-blur behaviour. There are reliable techniques for each problem — inspecting through the toolbar, loading the popup as a tab, pausing on startup, and capturing errors somewhere that outlives the popup. This guide collects them. It belongs to debugging extension contexts.

Why popups are hard to debug

A popup is an extension page shown in a browser-owned bubble. It is created when the user clicks the action and destroyed when it loses focus — including focus moving to a separate DevTools window. While DevTools is attached through “Inspect popup”, Chrome keeps the popup open even when focus moves to DevTools, which is the main trick. Startup problems are different: a script error, a CSP violation or a failed import happens in the first milliseconds, often leaving a blank popup that closes before any DevTools can attach. For those, you need the errors captured elsewhere or a way to run the same page where it does not close.

Which popup debugging technique to useDecision tree: for interactive debugging use Inspect popup, which keeps the popup open; for startup errors open popup.html in a tab or check the extension's Errors page; for size or layout issues open in a tab at popup dimensions; for behaviour depending on the active tab use Inspect popup on the real tab.What is going wrong?logic / interactionInspect popupkeeps it openblank or crashes on openpopup.html in a tab+ Errors pagePause on startdebugger / breakpointlayout / sizeTab at popup sizedevice toolbar
Inspect popup for interaction, a tab for startup, the Errors page for what you missed.

Step-by-step: debugging popups

1. Use Inspect popup for interactive debugging

Right-click the extension’s toolbar icon and choose Inspect popup (in Chrome and Edge; the item appears for unpacked extensions and when developer mode is on). The popup opens with DevTools attached, and it stays open while DevTools has focus, so you can set breakpoints, step through code and edit the DOM. Closing DevTools closes the popup. This is the right tool for everything except startup errors.

2. Open the popup page in a tab for startup problems

1chrome-extension://<extension-id>/popup.html

Execution context: a normal browser tab. Find the ID on chrome://extensions (or log chrome.runtime.id). As a tab, the page never closes on blur, DevTools can be opened before the page loads (open DevTools, then reload), and the Console shows every startup error. Differences to remember: there is no activeTab grant, chrome.tabs.query({active: true, currentWindow: true}) returns the popup’s own tab, and window.close() closes the tab. Pass a test tab ID through the URL if the popup needs one.

Popup debugging techniques comparedInspect popup, opening popup.html in a tab, the extensions Errors page and a startup error logger compared on whether they capture startup errors, support breakpoints and reflect the real popup environment.TechniqueStartup errorsBreakpointsReal environmentInspect popupPartially (reload)YesYespopup.html in a tabYesYesNo activeTab, no closeExtensions Errors pageYes (after the fact)NoYesStartup error loggerYes, persistedNoYes
Combine them: no single technique covers every popup bug.

3. Reload with DevTools attached to catch early errors

With the popup open via Inspect popup, press Ctrl+R / ⌘R in its DevTools window. The popup document reloads while DevTools stays attached, so the Console and Sources panels capture everything from the first line. Combine with “Pause on uncaught exceptions” in the Sources panel to stop exactly where startup fails.

4. Pause at startup deliberately

1// popup.js — temporary, development only
2if (new URLSearchParams(location.search).has("debug")) debugger;

Execution context: the popup page. Opening popup.html?debug in a tab with DevTools open pauses at the first line, before any of your initialisation runs. In the real popup, a debugger statement only pauses if DevTools is already attached, so it is harmless if left behind, but remove it before release.

Catching a startup crashThe popup opens blank and closes; the developer checks the extensions Errors page and sees an uncaught TypeError; opens popup.html in a tab with DevTools and Pause on exceptions; reloads; DevTools stops at the failing line where storage returned undefined.DeveloperErrors pagepopup.html tabopen chrome://extensions → ErrorsTypeError: items.map … popup.js:42open popup.html, DevTools, pause on exceptionspaused at popup.js:42 (items undefined)
Errors page to find it, a tab to reproduce it, the debugger to fix it.

5. Check the Errors page and the console of the right context

On chrome://extensions, the extension’s Errors button lists uncaught errors and console errors from all contexts, including popups that have already closed, with stack traces and the context they came from. Errors from the popup do not appear in the service worker’s console — each context has its own. See reading errors from the extensions page.

6. Log startup errors somewhere that outlives the popup

1// popup-errors.js — loaded first in popup.html
2addEventListener("error", (e) => persist({ msg: e.message, src: e.filename, line: e.lineno, stack: e.error?.stack }));
3addEventListener("unhandledrejection", (e) => persist({ msg: String(e.reason), stack: e.reason?.stack }));
4function persist(entry) {
5  chrome.storage.session.get({ popupErrors: [] }).then(({ popupErrors }) =>
6    chrome.storage.session.set({ popupErrors: [...popupErrors.slice(-19), { ...entry, at: Date.now() }] }));
7}

Execution context: the popup, loaded as the first script. Persisting the last 20 errors in storage.session lets you read them from the service worker console or the options page after the popup has closed — invaluable for intermittent failures. In production builds, send them to your error reporting instead; see capturing uncaught errors in every context.

7. Debug layout at the real size

Open popup.html in a tab, open DevTools, and use the device toolbar to set a custom size such as 380×560 — or set the tab’s window size with a small script. Chrome popups size to their content within limits (up to 800×600), so layout bugs often only appear at the real constraints. See fixing popup size and overflow issues.

8. Reproduce in an automated test

1// Playwright
2const popup = await context.newPage();
3await popup.goto(`chrome-extension://${extensionId}/popup.html`);
4const errors = [];
5popup.on("pageerror", (e) => errors.push(e));
6await popup.reload();
7expect(errors).toEqual([]);

Execution context: an end-to-end test. Loading the popup page in a tab under Playwright gives a reproducible environment for startup errors, and a test that asserts no page errors catches regressions. See testing a popup and options page with Playwright.

Common mistakes

  • Clicking into DevTools without Inspect popup. The popup closes.
  • Looking in the service worker console for popup errors. Each context has its own console.
  • Assuming the tab behaves exactly like the popup. No activeTab, different active tab.
  • Missing startup errors. Reload with DevTools attached, or persist errors.
  • Leaving debugger statements in releases. Remove them.

Cross-browser variation

  • Chrome / Edge: Inspect popup on the toolbar icon; Errors page on chrome://extensions.
  • Firefox: open about:debugging, Inspect the extension, and enable “Disable popup auto-hide” from the toolbox’s ⋯ menu to keep popups open while debugging.
  • Safari: enable the Develop menu, then Develop → Web Extension Background Content / the extension’s pages; popovers can be inspected via Web Inspector, and keeping them open may need the inspector attached first.

Verification

  1. Introduce a deliberate startup error and confirm it appears on the Errors page and in the tab console.
  2. Inspect popup, set a breakpoint in a click handler and confirm the popup stays open while paused.
  3. Reload the inspected popup and confirm startup logs appear.
  4. Confirm the persisted error list contains the last popup error.

FAQ

Why does Inspect popup not appear?

It appears for extensions loaded unpacked or with developer mode on. Packed store installs may not offer it.

Can I keep the popup open without DevTools?

Not in Chrome. Firefox’s “Disable popup auto-hide” is the closest equivalent.

Why does the popup work in a tab but not as a popup?

Usually because of activeTab-dependent code, the active tab query, or size constraints. Test those paths in the real popup.

Do console logs from the popup persist after it closes?

Not in its console — that DevTools window closes with it. Errors still appear on the extensions Errors page, and anything you persisted yourself remains in storage.

Other Testing, Debugging & Performance Optimization Resources