Description of the bug
HeapSnapshotManager.#loadSnapshot creates a HeapSnapshotWorkerProxy (a live worker) before it reads the snapshot file, but the manager only ever disposes workers that are tracked in the #snapshots map:
// src/HeapSnapshotManager.ts
async #loadSnapshot(absolutePath, uid) {
const workerProxy = new ...HeapSnapshotWorkerProxy(...); // spawns a worker
const loaderProxy = workerProxy.createLoader(uid, ...);
const fileStream = fsSync.createReadStream(absolutePath, {...});
for await (const chunk of fileStream) { // throws ENOENT for a missing/unreadable file
await loaderProxy.write(chunk);
}
await loaderProxy.close();
const snapshot = await snapshotPromise;
return {snapshot, worker: workerProxy};
}
getSnapshot has no try/catch and only calls #snapshots.set(...) on success. So when a load fails, the worker is never added to #snapshots, and therefore neither dispose(filePath) nor disposeAll() (added in #2428 for context teardown) can ever reach it. The worker leaks for the life of the process — one worker per failed load.
verifyFilesSchema: ['filePath'] on the read tools only checks the path is inside the workspace roots; it does not verify the file exists or is a valid snapshot.
Reproduction
- Call any of
get_heapsnapshot_summary / get_heapsnapshot_details / get_heapsnapshot_class_nodes / get_heapsnapshot_retainers / get_heapsnapshot_retaining_paths / get_heapsnapshot_edges with a filePath that is inside a workspace root but does not exist (or is a truncated / non-heapsnapshot file).
- The call fails, and the worker created for it is never disposed.
- Repeat → workers accumulate.
A direct unit test that fails on main:
const disposeSpy = sinon.spy(
DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy.prototype,
'dispose',
);
const manager = new HeapSnapshotManager();
await assert.rejects(manager.getSnapshot('/nonexistent/does-not-exist.heapsnapshot'));
sinon.assert.calledOnce(disposeSpy); // 0 calls on main
Expectation
A failed snapshot load disposes its worker rather than leaking it.
Fix: wrap the load body in try/catch and workerProxy.dispose() before rethrowing. I have this plus the regression test passing locally and can open a PR.
Chrome DevTools MCP version
main @ 574c320 (1.6.0)
Chrome version
n/a (independent of Chrome version)
Description of the bug
HeapSnapshotManager.#loadSnapshotcreates aHeapSnapshotWorkerProxy(a live worker) before it reads the snapshot file, but the manager only ever disposes workers that are tracked in the#snapshotsmap:getSnapshothas no try/catch and only calls#snapshots.set(...)on success. So when a load fails, the worker is never added to#snapshots, and therefore neitherdispose(filePath)nordisposeAll()(added in #2428 for context teardown) can ever reach it. The worker leaks for the life of the process — one worker per failed load.verifyFilesSchema: ['filePath']on the read tools only checks the path is inside the workspace roots; it does not verify the file exists or is a valid snapshot.Reproduction
get_heapsnapshot_summary/get_heapsnapshot_details/get_heapsnapshot_class_nodes/get_heapsnapshot_retainers/get_heapsnapshot_retaining_paths/get_heapsnapshot_edgeswith afilePaththat is inside a workspace root but does not exist (or is a truncated / non-heapsnapshot file).A direct unit test that fails on
main:Expectation
A failed snapshot load disposes its worker rather than leaking it.
Fix: wrap the load body in try/catch and
workerProxy.dispose()before rethrowing. I have this plus the regression test passing locally and can open a PR.Chrome DevTools MCP version
main @ 574c320 (1.6.0)
Chrome version
n/a (independent of Chrome version)