Skip to content

process.finalization removes extra refs during cleanup #64086

Description

@trivikr

Version

main

Platform

macOS 26.5.0

Subsystem

process

What steps will reproduce the bug?

import process from 'node:process';
import { setImmediate } from 'node:timers/promises';

const kept = [];

function onFinalize(ref) {
  console.log(`finalized: ${ref.name}`);
}

function setup() {
  const first = { name: 'first' };
  let collected = { name: 'collected' };
  const third = { name: 'third' };

  kept.push(first, third);

  process.finalization.register(first, onFinalize);
  process.finalization.register(collected, onFinalize);
  process.finalization.register(third, onFinalize);

  collected = null;
}

setup();

// Give V8 a few chances to collect `collected` and run the
// FinalizationRegistry cleanup before process exit.
for (let i = 0; i < 10; i++) {
  gc();
  await setImmediate();
}

console.log('expected: finalized: first');
console.log('expected: finalized: third');

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior? Why is that the expected behavior?

expected: finalized: first
expected: finalized: third
finalized: first
finalized: third

Both first and third are strongly reachable through kept, so both should remain registered and run their finalization callbacks during process exit.

What do you see instead?

expected: finalized: first
expected: finalized: third
finalized: first

After collected is garbage-collected, cleanup removes its ref and also removes third because splice(1, 2) deletes two entries. As a result, third is still alive but its exit callback is skipped.

Additional information

Noticed while working on #64085

The docs for process.finalization.register(ref, callback) say that it "registers a callback to be called when the process emits the exit event if the ref object was not garbage collected."

Metadata

Metadata

Assignees

Labels

processIssues and PRs related to the process subsystem.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions