Sebastian Gomez
How to Debug Chrome Extensions: A Step by Step Guide
Debugging Chrome extensions can be a challenge, especially for those just starting out in extension development. In this post we'll explore how to effectively debug your Chrome extensions and solve the most common problems that can come up during development. Let's go through it step by step.
Getting Started with Debugging
Manifest file: The manifest.json file is crucial for any Chrome extension. A very common mistake is having an incorrect configuration in this file.
A typical error example: make sure the properties are spelled correctly. For instance, the permissions property must be plural and the manifest version must be accurate.
These days Chrome only runs extensions built with Manifest V3. Manifest V2 was phased out gradually through 2024 and 2025, and it no longer works on any browser channel. That's why your manifest must always declare "manifest_version": 3. As you may remember from earlier chapters in this series, we dedicated a whole chapter to Manifest V3, so here we follow that same line.
{
"name": "My Extension",
"version": "1.0",
"manifest_version": 3,
"permissions": ["tabs"]
}Common manifest errors:
- Incorrect permissions: make sure permissions are in an array, not an object.
- Versions: when you update your extension, increment the
versionnumber appropriately to reflect major or minor changes. - Outdated manifest version: if you're coming from an old extension with
"manifest_version": 2, you must migrate it to version 3, because MV2 no longer runs.
Debugging the Service Worker
In Manifest V3, the background script was replaced by a Service Worker. It can shut down when it's not in use, which sometimes makes debugging harder: if you open it and see no activity, it may simply be idle.
Inspecting the Service Worker:
- Open
chrome://extensions/and turn on Developer mode in the top right corner. - Find your extension and click the "Inspect views: service worker" link. This opens DevTools connected directly to the Service Worker, with its console and breakpoints.
- For a low level view of every registered Service Worker, visit
chrome://serviceworker-internals. From there you can see their state, stop them, or force them to start.
Reactivating the Service Worker: reload the extension from chrome://extensions/ (the reload button) to make sure the Service Worker is active again, and watch its state in the console to confirm it's running.
Note: the Service Worker idle timeout was extended compared to the first MV3 releases. As long as there are active message or event ports, the inactivity timer resets, so a Service Worker that's processing messages won't shut down mid task. This greatly improves the debugging experience over the early MV3 versions.
Using the chrome.runtime Library
The chrome.runtime library is essential for handling messages and events within your extension.
Connection errors: a very common error is "Could not establish connection. Receiving end does not exist.". It usually shows up when sending messages between the Service Worker, content scripts, and the popup.
The most frequent real causes are:
- No listener is registered. The receiving end must register
chrome.runtime.onMessage. If that listener doesn't exist, there's no one to receive the connection. - The content script hasn't been injected yet. If you call
chrome.tabs.sendMessageon a tab before its content script has loaded, there's no receiver ready. Make sure the tab and content script are available before sending the message.
The fix: register the listener on the receiver and verify the target is ready before sending. In Manifest V3, chrome.runtime.sendMessage supports promises, so I recommend using the promise based version instead of callbacks, because it makes error handling much clearer.
// Receiver: register the listener
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === "PING") {
sendResponse({ ok: true });
}
});
// Sender: promise based sendMessage (MV3)
const response = await chrome.runtime.sendMessage({ type: "PING" });
console.log(response);Debugging the Extension Popup
The extension popup is the interface users see when they interact with your extension. It's important to make sure it works correctly.
Opening the developer tools:
- Right click the popup and select "Inspect" to open Chrome DevTools connected to the popup.
- Use the "Console" tab to see errors and
console.logmessages.
Debugging AJAX and the network: use the "Network" tab to monitor requests and make sure the responses are what you expect.
Debugging the Content Script
The content script interacts directly with web pages. To debug it:
Developer console:
- Open the page's developer tools and go to the "Sources" tab.
- Select your content script and use breakpoints to pause execution and inspect the state of the DOM.
Monitoring Permissions
Make sure your extension has the right permissions to carry out the operations it needs.
Declaring permissions: in the manifest.json file, declare all the permissions you need.
{
"permissions": ["tabs", "storage", "contextMenus"]
}Note: tabs is a fairly broad permission. If you only need access to the active tab when the user interacts with your extension, prefer activeTab: it asks less of your users and reduces the permission surface Chrome shows at install time.
Reviewing Other People's Extensions
To learn and get inspiration, you can review the code of other installed extensions.
Accessing installed extensions:
- On macOS:
Library/Application Support/Google/Chrome/Default/Extensions. - On Windows:
C:\Users\[YourUser]\AppData\Local\Google\Chrome\User Data\Default\Extensions.
Analyzing the code: examine files like manifest.json and the scripts to understand how they work and how they're structured.
Suggested exercises
- Take a sample extension with
"manifest_version": 2and migrate its manifest to Manifest V3, adjusting the background script into a Service Worker. - Deliberately trigger the
"Could not establish connection. Receiving end does not exist."error by messaging a tab with no content script, then fix it by registering anonMessagelistener. - Swap the
tabspermission foractiveTabin a simple extension and verify it still works for your use case.
3-point summary
- In 2026 Chrome only runs Manifest V3 extensions, so your manifest must always declare
"manifest_version": 3. - Debug the Service Worker from
chrome://extensions/with "Inspect views: service worker", and checkchrome://serviceworker-internalsfor low level control. - Most messaging errors come from a missing
onMessagelistener or a content script that hasn't been injected; use the promise basedchrome.runtime.sendMessagefor clearer error handling.
I hope this post helped you better understand how to debug your Chrome extensions. If you have any questions or need more details, don't hesitate to leave a comment. See you next time!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.