This error occurs when attempting to send a message through a MessagePort that has already been closed or whose worker thread has terminated. The communication channel between the main thread and worker is no longer available.
This error indicates that your code is trying to call `postMessage()` on a MessagePort that has been closed or disconnected. In Node.js worker threads, MessagePorts provide a communication channel between the main thread and worker threads. Once either side of the channel closes the port (via `close()` method) or the worker terminates, the MessagePort becomes unusable. When a worker thread exits, terminates, or explicitly closes its message port, any subsequent attempts to send messages through that port will fail with this error. The error prevents data from being sent into a void where no receiver exists to process it. This commonly happens in scenarios where the main thread continues to send messages to a worker that has already completed its task and exited, or when cleanup code closes a port but other parts of the application still hold references to it and attempt communication.
Listen for worker exit and port close events to know when communication is no longer possible:
const { Worker } = require('worker_threads');
const worker = new Worker('./worker.js');
// Track when worker exits
worker.on('exit', (code) => {
console.log(`Worker exited with code ${code}`);
// Stop sending messages after this point
});
// Track when worker encounters an error
worker.on('error', (error) => {
console.error('Worker error:', error);
});
// Track when worker is terminated
worker.on('messageerror', (error) => {
console.error('Message error:', error);
});When using MessagePort directly, track the close event and maintain a flag:
const { MessageChannel } = require('worker_threads');
const { port1, port2 } = new MessageChannel();
let isPortOpen = true;
port1.on('close', () => {
isPortOpen = false;
console.log('Port closed');
});
// Safe message sending
function sendMessage(data) {
if (isPortOpen) {
port1.postMessage(data);
} else {
console.warn('Cannot send message: port is closed');
}
}Design workers to signal completion before exiting:
// main.js
const { Worker } = require('worker_threads');
const worker = new Worker('./worker.js');
let workerActive = true;
worker.on('message', (msg) => {
if (msg.type === 'DONE') {
workerActive = false;
worker.terminate();
}
});
function sendToWorker(data) {
if (workerActive) {
worker.postMessage(data);
} else {
console.log('Worker has completed, not sending message');
}
}
// worker.js
const { parentPort } = require('worker_threads');
parentPort.on('message', (data) => {
// Process data
});
// Signal completion before exit
parentPort.postMessage({ type: 'DONE' });Wrap postMessage calls in error handling to gracefully handle closed ports:
function safeSendMessage(worker, message) {
try {
worker.postMessage(message);
} catch (error) {
if (error.message.includes('closed message port')) {
console.warn('Worker port is closed, message not sent');
// Handle the closed port scenario
} else {
throw error; // Re-throw unexpected errors
}
}
}For robust applications, use a worker pool that tracks active workers:
class WorkerPool {
constructor(workerScript, poolSize) {
this.workers = [];
this.activeWorkers = new Set();
for (let i = 0; i < poolSize; i++) {
const worker = new Worker(workerScript);
this.workers.push(worker);
this.activeWorkers.add(worker);
worker.on('exit', () => {
this.activeWorkers.delete(worker);
});
}
}
sendToAvailableWorker(data) {
const availableWorker = Array.from(this.activeWorkers)[0];
if (availableWorker) {
availableWorker.postMessage(data);
return true;
} else {
console.warn('No active workers available');
return false;
}
}
terminate() {
this.workers.forEach(w => w.terminate());
this.activeWorkers.clear();
}
}MessagePort Transfer Semantics: When you transfer a MessagePort to a worker using postMessage(data, [port]), the original port becomes unusable in the sending context. Attempting to use it will also cause this error. Always track which context owns the port after transfer.
Event Loop Timing: Messages sent via postMessage() are asynchronous. If a worker exits immediately after processing, there's a race condition where the main thread might try to send a message after the worker has already terminated but before the exit event has been processed. Use explicit coordination (acknowledgment messages) for critical scenarios.
Memory Leaks: Failing to clean up worker references when they exit can cause memory leaks. Always remove event listeners and clear references when workers terminate.
Cluster vs Worker Threads: This error is specific to worker_threads MessagePort. If using the cluster module, you'll encounter different IPC (Inter-Process Communication) errors. The cluster module uses a different communication mechanism.
Debugging: Enable NODE_DEBUG=worker environment variable to see detailed worker thread lifecycle events that can help identify when and why ports are closing unexpectedly.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams