fix(nostr): clear per-relay publish timeout timer to prevent dangling handles#98720
Conversation
|
@clawsweeper review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Codex review: needs maintainer review before merge. Reviewed July 1, 2026, 12:57 PM ET / 16:57 UTC. Summary PR surface: Source +5, Tests +72. Total +77 across 2 files. Reproducibility: yes. Current main creates a setTimeout inside publishProfileEvent without retaining or clearing the handle, and the PR body includes a standalone real-Node timer probe showing dangling timers before the patch and none after. Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land one canonical Nostr profile timer cleanup that preserves this PR's regression coverage, then close the duplicate candidate and the linked issue through the merge. Do we have a high-confidence way to reproduce the issue? Yes. Current main creates a setTimeout inside publishProfileEvent without retaining or clearing the handle, and the PR body includes a standalone real-Node timer probe showing dangling timers before the patch and none after. Is this the best way to solve the issue? Yes. Clearing the timeout owned by publishProfileEvent in a finally block is the narrowest maintainable fix; changing nostr-tools or moving the timeout to the bus caller would be broader than needed for this local timer lifecycle. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against db97bc1e4fcf. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +5, Tests +72. Total +77 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
6e58eee to
c1d47eb
Compare
|
Land-ready at rebased head
This is the strongest canonical fix in the duplicate cluster; #98135 has the same source shape without equivalent regression coverage. |
|
Merged via squash.
|
* origin/main: fix(nostr): clear per-relay publish timeout timer to prevent dangling handles (#98720)
… handles (openclaw#98720) (cherry picked from commit a7a444e)
What Problem This Solves
Closes #98463
publishProfileEventinextensions/nostr/src/nostr-profile.tscreates asetTimeoutper relay to enforceRELAY_PUBLISH_TIMEOUT_MS, but the returned timer handle was discarded. When a relay accepted the event quickly (the common success path), the timeout callback had already been scheduled and would remain active until it fired 5 seconds later. On busy agents that publish profiles repeatedly, these dangling timers accumulate and increase memory/FD pressure.Why This Change Was Made
The fix saves the
setTimeouthandle and clears it in afinallyblock, so the timer is cancelled whether the publish succeeds, times out, or throws. This matches the timer lifecycle already used in sibling Nostr modules (nostr-bus.tsandnostr-profile-import.ts).User Impact
Evidence
Unit tests in
extensions/nostr/src/nostr-profile.test.ts:Run:
Result: 34 passed.
Lint:
node scripts/run-oxlint.mjs extensions/nostr/src/nostr-profile.ts extensions/nostr/src/nostr-profile.test.ts→ exit 0.Typecheck:
pnpm tsgo:extensions:test→ exit 0.Build:
pnpm build→ exit 0.Real behavior proof
I wrote a standalone Node.js script that monkey-patches
setTimeout/clearTimeoutto count active "publish timeout" timers after the real exportedpublishProfile()function returns. The script runs against real Node timers (no Vitest fake timers).BEFORE (source fix reverted):
The leak appears only on the success path: when
pool.publishresolves before the 5 s timeout, the old code never cleared the timer.AFTER (this patch):
Negative control: reverting only the source change while keeping the new tests makes the success-path test fail to observe a clean timer state, confirming the tests are sensitive to the fix.
setTimeouthandle created duringpublishProfileis now cleared after the race finishes.node scripts/run-vitest.mjs extensions/nostr/src/nostr-profile.test.ts --run.RELAY_PUBLISH_TIMEOUT_MStimers remain afterpublishProfilereturns.Related issues