How to fix Error: Performance observer for this entry type already exists in Node.js
This error occurs when attempting to register multiple PerformanceObserver instances for the same entry type in Node.js. Only one observer can monitor a specific entry type at a time, and duplicate registrations trigger an assertion failure that can crash the process.
Quick fix
Fixes Error: Performance observer for this entry type already exists
// Create a global registry for observers
const observerRegistry = new Map();
function getOrCreateObserver(entryType, callback) {
if (observerRegistry.has(entryType)) {
return observerRegistry.get(entryType);
}
const observer = new PerformanceObserver(callback);
observer.observe({ entryTypes: [entryType] });
observerRegistry.set(entryType, observer);
return observer;
}
// Usage
const gcObserver = getOrCreateObserver('gc', (list) => {
console.log('GC events:', list.getEntries());
});- Check for existing observers before creating new ones
- Disconnect existing observers before creating new ones
- Use a single observer for multiple entry types
- Implement singleton pattern for observer modules
// Create a global registry for observersconst observerRegistry = new Map();function getOrCreateObserver(entryType, callback) {if (observerRegistry.has(entryType)) {return observerRegistry.get(entryType);}const observer = new PerformanceObserver(callback);observer.observe({ entryTypes: [entryType] });observerRegistry.set(entryType, observer);return observer;}// Usageconst gcObserver = getOrCreateObserver('gc', (list) => {console.log('GC events:', list.getEntries());});Error: Performance observer for this entry type already exists
What this error means
This error indicates that Node.js is preventing you from creating multiple PerformanceObserver instances that observe the same entry type (such as 'measure', 'mark', 'gc', 'function', or 'http'). The perf_hooks module enforces a constraint where each performance entry type can only be observed by one active observer at a time. When you call observer.observe() with an entryTypes array, Node.js checks if any of those types are already being monitored by another observer. If a duplicate is detected, the error is thrown to prevent conflicting observers that could lead to data corruption or unexpected behavior. This was particularly problematic in Node.js 16.0.0 where duplicate registrations would kill the process entirely with an assertion failure, though the behavior has been refined in later versions to throw a proper error instead.
Typical symptoms
- Application crashes immediately when creating a second PerformanceObserver
- Error thrown when calling observer.observe() with previously registered entry types
- Process exits with assertion failure in Node.js 16.x versions
- Performance monitoring code fails in modules loaded multiple times
- Hot reloading or module caching causes observer registration conflicts
Common causes
- Creating multiple PerformanceObserver instances that observe the same entry type
- Not calling disconnect() on a previous observer before creating a new one
- Module initialization code creating observers each time the module is required
- Hot module reloading in development recreating observers without cleanup
- Third-party libraries creating observers for the same entry types as your code
- Singleton pattern not properly implemented for performance observer instances
How to fix "Error: Performance observer for this entry type already exists"
Maintain a registry of active observers to prevent duplicate registrations:
// Create a global registry for observers
const observerRegistry = new Map();
function getOrCreateObserver(entryType, callback) {
if (observerRegistry.has(entryType)) {
return observerRegistry.get(entryType);
}
const observer = new PerformanceObserver(callback);
observer.observe({ entryTypes: [entryType] });
observerRegistry.set(entryType, observer);
return observer;
}
// Usage
const gcObserver = getOrCreateObserver('gc', (list) => {
console.log('GC events:', list.getEntries());
});This pattern ensures only one observer exists per entry type.
If you need to replace an observer, properly disconnect the old one first:
const { PerformanceObserver } = require('perf_hooks');
// Store reference to current observer
let currentObserver = null;
function setupPerformanceMonitoring(callback) {
// Disconnect previous observer if it exists
if (currentObserver) {
currentObserver.disconnect();
}
// Create new observer
currentObserver = new PerformanceObserver(callback);
currentObserver.observe({ entryTypes: ['measure', 'mark'] });
}
// Later, when shutting down:
function cleanup() {
if (currentObserver) {
currentObserver.disconnect();
currentObserver = null;
}
}Always call disconnect() to properly clean up observers.
Instead of creating separate observers, observe multiple entry types with one observer:
const { PerformanceObserver } = require('perf_hooks');
// Single observer for all entry types
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
switch (entry.entryType) {
case 'measure':
console.log('Measure:', entry.name, entry.duration);
break;
case 'mark':
console.log('Mark:', entry.name);
break;
case 'gc':
console.log('GC event:', entry.kind);
break;
}
});
});
// Observe multiple types at once
observer.observe({
entryTypes: ['measure', 'mark', 'gc']
});This approach eliminates the possibility of duplicate registrations.
If using observers in a module, ensure they're created only once:
// performance-monitor.js
const { PerformanceObserver } = require('perf_hooks');
let observerInstance = null;
function initializeMonitoring() {
if (observerInstance) {
console.log('Performance monitoring already initialized');
return observerInstance;
}
observerInstance = new PerformanceObserver((list) => {
// Handle performance entries
const entries = list.getEntries();
entries.forEach((entry) => {
console.log(`[${entry.entryType}] ${entry.name}`);
});
});
observerInstance.observe({
entryTypes: ['measure', 'mark', 'function', 'http']
});
console.log('Performance monitoring initialized');
return observerInstance;
}
function shutdown() {
if (observerInstance) {
observerInstance.disconnect();
observerInstance = null;
}
}
module.exports = { initializeMonitoring, shutdown };Call initializeMonitoring() once at application startup.
In development environments with hot reloading, clean up observers properly:
// performance-tracker.js
const { PerformanceObserver } = require('perf_hooks');
let observer = null;
function setupObserver() {
if (observer) {
observer.disconnect();
}
observer = new PerformanceObserver((list) => {
// Handle entries
});
observer.observe({ entryTypes: ['measure'] });
}
// Clean up on hot reload (for webpack/vite)
if (module.hot) {
module.hot.dispose(() => {
if (observer) {
observer.disconnect();
observer = null;
}
});
}
setupObserver();
module.exports = { observer };This prevents observers from accumulating during development.
Advanced notes
Version-specific behavior: In Node.js 16.0.0, duplicate observer registrations would trigger an assertion failure and immediately kill the process. Later versions (16.1.0+) improved this to throw a proper error instead, making it easier to catch and handle gracefully.
Entry type limitations: Each entry type ('measure', 'mark', 'gc', 'function', 'http', 'dns', 'net') can only have one active observer. This is a design decision to prevent performance overhead and conflicting callbacks.
Third-party conflicts: If using monitoring libraries (like APM tools or performance profilers), they may create their own PerformanceObserver instances. Check library documentation for configuration options to disable internal observers if you need to create your own.
Buffered vs. non-buffered: When using observe({ buffered: true }), you can retrieve historical entries, but this doesn't bypass the single-observer-per-type restriction. The buffered flag affects what entries are delivered, not how many observers can exist.
Process exit hooks: Always implement proper cleanup in process exit handlers to disconnect observers:
process.on('SIGTERM', () => {
if (observer) observer.disconnect();
process.exit(0);
});
process.on('SIGINT', () => {
if (observer) observer.disconnect();
process.exit(0);
});Testing considerations: In test environments where tests run in parallel or sequentially in the same process, ensure observers are created and destroyed properly between test runs to avoid conflicts.
Frequently asked questions
- What does "Error: Performance observer for this entry type already exists" mean?
- js is preventing you from creating multiple PerformanceObserver instances that observe the same entry type (such as 'measure', 'mark', 'gc', 'function', or 'http'). The perf_hooks module enforces a constraint where each performance entry type can only be observed by one active observer at a time. js checks if any of those types are already being monitored by another observer.
- How do I fix "Error: Performance observer for this entry type already exists"?
- This error occurs when attempting to register multiple PerformanceObserver instances for the same entry type in Node.js. Only one observer can monitor a specific entry type at a time, and duplicate registrations trigger an assertion failure that can crash the process.
- What causes "Error: Performance observer for this entry type already exists"?
- Creating multiple PerformanceObserver instances that observe the same entry type Not calling disconnect() on a previous observer before creating a new one Module initialization code creating observers each time the module is required
- Is the "Error: Performance observer for this entry type already exists" error serious?
- It is a medium-severity error โ more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- ๐Node.js Official Documentation
- ๐Node.js PerformanceObserver Documentation(Official Docs)
- ๐
- ๐MDN - PerformanceObserver Interface(Official Docs)