#692·comlink

In-flight requests are silently dropped when the proxy is GC'd mid-await

Author: nyan-leftCreated Apr 27, 2026Updated Apr 27, 2026

Hit this in production. Comlink.wrap(worker).slow() style calls intermittently hang forever on Chrome with no error and nothing in the console. The trigger is V8 collecting the transient proxy during the suspended await; under memory pressure it fires reliably.

When the proxy is collected, comlink's FinalizationRegistry cleanup runs releaseEndpoint, which sends RELEASE, closes the port, and clears pendingListeners, without checking whether any of those listeners are still mid-flight. The worker's reply lands on a closed port and the Promise never resolves.

MDN is pretty explicit about not relying on FR semantics for correctness:

Developers shouldn't rely on cleanup callbacks for essential program logic.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry

Repro

https://github.com/nyan-left/comlink-repro

Workaround

Pin the proxy on a longer-lived ref so it stays reachable across the await:

javascript
// Before, intermittently hangs
const result = await Comlink.wrap(worker).slow();

// After (works):
const proxy = Comlink.wrap(worker);
this._keepAlive = proxy;
try {
  return await proxy.slow();
} finally {
  this._keepAlive = undefined;
}

Awkward to ask every comlink user to remember though.

Proposed fix

Open PR: https://github.com/GoogleChromeLabs/comlink/pull/693

Related

  • #600 - Safari 16 GC'ing MessageChannel mid-callback. Same family of bug.
  • #601 - rejecting pending requests on explicit releaseProxy(). Related but doesn't cover GC-triggered path.

Source: GoogleChromeLabs/comlink