openclaw - 💡(How to fix) Fix Module-level `controllers` array is never cleared, accumulating stale entries on re-hydration [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#83917Fetched 2026-05-20 03:46:45
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
1
Timeline (top)
labeled ×3commented ×1unsubscribed ×1

Fix Action

Fix / Workaround

Severity: low / Confidence: medium / Category: bug Triage: risk Detected against: openclaw v2026.5.18 (latest stable at time of scan, 2026-05-18) Tooling: clawpatch 0.3.0 + acpx/claude-sonnet-4-5 via Brad Mills protocol


Standardized clawpatch finding. Persistent in v2026.5.18 (not resolved by upgrading from v2026.5.12). Finding ID: fnd_sig-feat-cli-command-1f351e9797-_a7eac11fd0.

Code Example

const controllers: DiffController[] = [];

---

controllers.push(controller);
RAW_BUFFERClick to expand / collapse

Severity: low / Confidence: medium / Category: bug Triage: risk Detected against: openclaw v2026.5.18 (latest stable at time of scan, 2026-05-18) Tooling: clawpatch 0.3.0 + acpx/claude-sonnet-4-5 via Brad Mills protocol

Evidence

  • extensions/diffs/src/viewer-client.ts:22-22 (controllers)
const controllers: DiffController[] = [];
  • extensions/diffs/src/viewer-client.ts:307-307 (hydrateViewer)
controllers.push(controller);

Reasoning

controllers is a module-level mutable array. hydrateViewer() only appends to it and never resets it before iterating cards. In a normal single-load browser context this is harmless, but in environments where the module is re-evaluated (bundler HMR, Vitest's module isolation, or programmatic re-import) a second invocation of hydrateViewer() doubles the controller list. Thereafter every syncAllControllers() call (triggered by any toolbar button click) will call applyState() on stale, already-detached FileDiff instances, producing errors and leaking event listeners and DOM references.

Reproduction

In Vitest with vi.resetModules() between two imports of viewer-client.ts, call main() twice in the same jsdom document. After the second call, controllers.length will be twice the number of cards.

Recommendation

Reset controllers at the start of hydrateViewer() (e.g., controllers.length = 0) or scope it to the function and expose it via a closure only to syncAllControllers. If live replacement is needed, also call .destroy() on evicted FileDiff instances if the @pierre/diffs API provides one.

Why existing tests miss this

No test exercises the viewer-client hydration lifecycle directly. The runtime bundle is only smoke-checked for string presence in config.test.ts.

Suggested regression test

Unit test that calls hydrateViewer twice and asserts controllers contains exactly as many entries as the number of diff cards in the DOM, not twice that many.

Minimum fix scope

Add controllers.length = 0; (or controllers.splice(0)) as the first statement inside hydrateViewer.


Standardized clawpatch finding. Persistent in v2026.5.18 (not resolved by upgrading from v2026.5.12). Finding ID: fnd_sig-feat-cli-command-1f351e9797-_a7eac11fd0.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Module-level `controllers` array is never cleared, accumulating stale entries on re-hydration [1 comments, 2 participants]