PGliteWorker can post to a closed BroadcastChannel during teardown
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- typescript
- Domain
- distributed-systems, testing-qa, web-dev
Research direction
Start with the PGliteWorker teardown path, especially #leaderNotifyLoop(), close(), and the leader-here handler shown in the issue. Reproduce the startup and leader-change cases, then add regression coverage that verifies close() leaves no timer accessing a closed BroadcastChannel and produces no unhandled rejection.
Written by the indexing model from the issue text.
Description
Reproduction
Reproduced with @electric-sql/pglite 0.5.0, 0.5.6, 0.5.7, and 0.5.8 in Chromium.
pglite.worker.ts:
import { PGlite } from "@electric-sql/pglite";
import { worker } from "@electric-sql/pglite/worker";
worker({
async init(options) {
return new PGlite({ dataDir: options.dataDir });
},
});
main.ts:
import { PGliteWorker } from "@electric-sql/pglite/worker";
import PGliteWorkerScript from "./pglite.worker?worker";
window.addEventListener("unhandledrejection", (event) => {
console.error("Unhandled rejection:", event.reason);
});
const db = await PGliteWorker.create(
new PGliteWorkerScript({ type: "module" }),
{ id: "broadcast-channel-close-repro" },
);
console.log("ready at close:", db.ready); // Usually false
await db.close();
await new Promise((resolve) => setTimeout(resolve, 100));
db.ready is commonly false when close() runs. The error is asynchronous, so wait briefly after closing.
Expected
close() should not produce an uncaught exception.
Actual
Shortly after closing:
InvalidStateError: Failed to execute 'postMessage' on 'BroadcastChannel': Channel is closed
In a multi-tab handoff this may also coincide with LeaderChangedError.
Cause
The notification loop schedules a timer while disconnected:
async #leaderNotifyLoop() {
if (!this.#connected) {
this.#broadcastChannel!.postMessage({
type: "tab-here",
id: this.#tabId,
});
setTimeout(() => this.#leaderNotifyLoop(), 16);
}
}
close() closes the channel but does not cancel or guard the timer:
async close() {
if (this.#closed) return;
this.#closed = true;
this.#broadcastChannel?.close();
this.#tabChannel?.close();
this.#releaseTabCloseLock?.();
this.#workerProcess.terminate();
}
The timer callback discards the async loop's promise, so postMessage() throwing becomes an unhandled rejection. The leader-here handler can re-arm the same loop during handoff.
Request
Please ensure that after close() resolves no internal timer accesses a closed channel or produces an unhandled rejection, with a regression test covering startup and leader-change teardown.
This is distinct from PR #1109, which addresses RPCs hanging across leader changes.
- Dominant language
- TypeScript
- Stars
- 16.2k
- Forks
- 461
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 23
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from electric-sql/pglite
-
[BUG]: pglite-socket: handlers that end in an error are never removed, so maxConnections fills up and every new connection is refusedPossibly taken @PrabalParihar claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
electric-sql/pglite#1155 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
electric-sql/pglite#1145 ·
Maintainers usually reply within 1 day
-
Feature request: Nitro 3 support (`unwasm` export condition)May be free again A pull request for this issue was closed without being merged. Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
electric-sql/pglite#1120 · 16 comments ·
Maintainers usually reply within 1 day
-
[BUG]: pglite-sync: after a `must-refetch` a synced table can stay empty; commit errors are swallowedPossibly taken @jeromerg claimed this 16 days ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 45/100
electric-sql/pglite#1125 ·
Maintainers usually reply within 1 day
-
Worker: init() rejection is never propagated to clientsPossibly taken @tdrz claimed this 11 days ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 72/100
electric-sql/pglite#1122 · 1 reaction · 1 assignee ·
Maintainers usually reply within 1 day
All issues in electric-sql/pglite
Similar issues
-
[Bug]: Device screenshot fails with 500 on iPhone Duo (Xcode 27.2): serve-sim calls simctl without --displayPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
pingdotgg/t3code#24900 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
inu-appcenter/UNIDorm-web#411 ·
Maintainers usually reply within 1 day
-
🐛 bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
ranxianglei/billion-context#4720 ·
Maintainers usually reply within 1 day
-
area:arcade game p3-medium ready-for-agent type:bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
fderuiter/deruiter.dev#2840 ·
Maintainers usually reply within 1 day