Skip to content

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

Description

@nyan-left

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:

// 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: #693

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions