In-depth Analysis and Solution for "No resource with given identifier found" Error with Network.getResponseBody in Chrome Extensions

Dec 07, 2025 · Programming · 11 views · 7.8

Keywords: Chrome extension | Network.getResponseBody | Debugger API

Abstract: This article explores the "No resource with given identifier found" error encountered when using the Network.getResponseBody API in Chrome extension development. By analyzing issues in the original code, such as premature debugger detachment and request-response mismatches, it proposes an optimized solution based on event queue management. The article details how to track Network.requestWillBeSent and Network.responseReceived events to precisely match requests with responses, ensuring getResponseBody is called at the appropriate time to avoid resource identifier errors. Additionally, it discusses best practices for memory management, like single debugger attachment and conditional detachment, to enhance extension stability and performance.

Problem Background and Error Analysis

In Chrome extension development, developers often use the chrome.debugger API to intercept and analyze network requests, particularly through the Network.getResponseBody method to retrieve HTTP response bodies. However, many encounter the error: Unchecked runtime.lastError while running debugger.sendCommand: {"code":-32000,"message":"No resource with given identifier found"}. This error typically results in the body parameter being undefined, preventing the extension from accessing response data.

Issues in the Original Code

The core logic of the original code is as follows: In the chrome.webRequest.onBeforeRequest listener, when a specific URL (e.g., containing /mypage) is detected, it attaches a debugger and enables network monitoring. Then, in the chrome.debugger.onEvent listener, when the Network.loadingFinished event fires, it attempts to call Network.getResponseBody to get the response body and detaches the debugger upon completion.

// Original code snippet
chrome.debugger.onEvent.addListener(function (source, method, params) {
    if (method == "Network.loadingFinished") {
        chrome.debugger.sendCommand(
            source,
            "Network.getResponseBody",
            {"requestId": params.requestId},
            function (body) {
                console.log(body);
                chrome.debugger.detach(source); // Premature debugger detachment
            });
    }
});

The main issues are:

  1. Premature Debugger Detachment: Detaching the debugger immediately in the Network.loadingFinished event may prevent subsequent requests from being handled correctly, as the debugger is no longer listening to network events.
  2. Request-Response Mismatch: Websites may send multiple responses, and the code does not precisely match target requests with responses, leading to attempts to access non-existent resource identifiers.

Solution: Event Queue-Based Optimization

To address these issues, we introduce an event queue management system that ensures Network.getResponseBody is called at the correct time. The optimized code tracks Network.requestWillBeSent and Network.responseReceived events to build request and response queues, enabling precise resource matching.

Core Implementation Steps

  1. Single Debugger Attachment: Use a global variable gAttached to ensure the debugger is attached only once, avoiding resource waste or conflicts. Attach the debugger on the first request via the chrome.webRequest.onBeforeRequest listener and then remove this listener.
  2. var gAttached = false;
    var initialListener = function (details) {
        if (gAttached) return;
        var tabId = details.tabId;
        if (tabId > 0) {
            gAttached = true;
            chrome.debugger.attach({ tabId: tabId }, "1.0", function () {
                chrome.debugger.sendCommand({ tabId: tabId }, "Network.enable");
            });
            chrome.webRequest.onBeforeRequest.removeListener(initialListener);
        }
    };
    chrome.webRequest.onBeforeRequest.addListener(initialListener, {urls: ["<all_urls>"]}, ["blocking"]);
  3. Building Request and Response Queues: In the chrome.debugger.onEvent listener, handle Network.requestWillBeSent and Network.responseReceived events separately. When target URLs are detected, push request URLs into the gRequests queue and response information (including requestId) into the gObjects queue.
  4. var gRequests = [];
    var gObjects = [];
    
    chrome.debugger.onEvent.addListener(function (source, method, params) {
        if (method == "Network.requestWillBeSent") {
            var rUrl = params.request.url;
            if (getTarget(rUrl) >= 0) {
                gRequests.push(rUrl);
            }
        }
        if (method == "Network.responseReceived") {
            var eUrl = params.response.url;
            var target = getTarget(eUrl);
            if (target >= 0) {
                gObjects.push({
                    requestId: params.requestId,
                    target: target,
                    url: eUrl
                });
            }
        }
        // Handle loadingFinished event
    });
  5. Precise Matching and Response Body Retrieval: In the Network.loadingFinished event, search for a matching response object in the gObjects queue using requestId. If found, call Network.getResponseBody and remove related entries from the queues after processing. This ensures each request is handled after its response is fully loaded, avoiding resource identifier errors.
  6. if (method == "Network.loadingFinished" && gObjects.length > 0) {
        var requestId = params.requestId;
        var object = null;
        for (var o in gObjects) {
            if (requestId == gObjects[o].requestId) {
                object = gObjects.splice(o, 1)[0];
                break;
            }
        }
        if (object == null) {
            console.log('Failed!!');
            return;
        }
        gRequests.splice(gRequests.indexOf(object.url), 1);
        chrome.debugger.sendCommand(
            source,
            "Network.getResponseBody",
            {"requestId": requestId},
            function (response) {
                if (response) {
                    dispatch(source.tabId, object.target, JSON.parse(response.body));
                } else {
                    console.log("Empty response for " + object.url);
                }
                // Conditional debugger detachment
                if (gRequests.length == 0) {
                    chrome.debugger.detach({ tabId: source.tabId }, function () {
                        chrome.debugger.attach({ tabId: source.tabId }, "1.0", function () {
                            chrome.debugger.sendCommand({ tabId: source.tabId }, "Network.enable");
                        });
                    });
                }
            });
    }
  7. Conditional Debugger Detachment: Detach and reattach the debugger only after all target requests are processed (i.e., when the gRequests queue is empty). This prevents memory leaks while keeping the debugger active when needed.

Supplementary References and Best Practices

Beyond the optimized solution, other answers provide valuable insights. For example, one suggests adding a delay (e.g., using time.sleep) before calling Network.getResponseBody to ensure the request is complete. While this might work in some asynchronous scenarios, event-driven queue management is preferred for its precision and avoidance of unnecessary waits.

Key best practices include:

Conclusion

By refactoring the code to adopt an event queue management system, developers can effectively resolve the "No resource with given identifier found" error. This approach not only enhances code reliability but also optimizes resource management, enabling Chrome extensions to stably retrieve and process HTTP response bodies. In practice, adjusting target URL filtering and event handling logic based on specific needs can further extend the applicability of this solution.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.