Skip to content

fix: reduce listener leak from per-part show-checkmarks subscriptions#302589

Open
bryanchen-d wants to merge 2 commits intomainfrom
bryanchen-d/fix-listener-leak-show-checkmarks
Open

fix: reduce listener leak from per-part show-checkmarks subscriptions#302589
bryanchen-d wants to merge 2 commits intomainfrom
bryanchen-d/fix-listener-leak-show-checkmarks

Conversation

@bryanchen-d
Copy link
Contributor

@bryanchen-d bryanchen-d commented Mar 17, 2026

Summary

Fixes #301186

Reduces listener accumulation on the configuration service's onDidChangeConfiguration emitter by consolidating per-part show-checkmarks class toggles into a single subscription at the row container level.

Is this a real leak?

No — this is a false positive from the LeakageMonitor, not a true memory leak. All the per-part onDidChangeConfiguration subscriptions are properly registered via this._register() and are correctly disposed when their owning parts are disposed. The issue is that VS Code's LeakageMonitor (in event.ts) fires a ListenerLeakError when any single emitter exceeds a global listener threshold (175, set in workbench.ts). In agentic sessions with 200+ simultaneous tool invocations, the sheer number of simultaneously alive listeners on the configuration service emitter (475+) crosses that threshold — even though none of them actually leak.

That said, the fix is still worthwhile:

  • It eliminates telemetry noise from spurious ListenerLeakError reports
  • It reduces redundant work — instead of 200+ identical subscriptions doing the same CSS class toggle, a single subscription at the row container level handles it for all child parts
  • It simplifies the code by removing duplicated logic across 4 different components

What scenarios may trigger the error

Agentic chat sessions with many tool invocations (100+). Each tool invocation creates a ChatToolProgressSubPart, ChatTerminalToolProgressPart, or CollapsedCodeBlock, each individually subscribing to configurationService.onDidChangeConfiguration for the ShowChatCheckmarks accessibility setting. With 200+ tool invocations and additional subscribers from other VS Code components, the total listener count on the configuration service emitter exceeds the global leak warning threshold (175), triggering the '[044] potential listener LEAK detected' error.

Code flow explaining why the error gets thrown and goes unhandled

  1. Each ChatToolProgressSubPart, ChatTerminalToolProgressPart (non-collapsible case), CollapsedCodeBlock, and renderConfirmationAction subscribes to configurationService.onDidChangeConfiguration in its constructor/method to toggle the show-checkmarks CSS class.
  2. In an agentic session, a single chat response can contain 100+ tool invocations, each creating one or more of these parts.
  3. Each subscription adds a listener to the configuration service's internal _onDidChangeConfiguration Emitter.
  4. Combined with existing listeners from other VS Code components (~100-150), the total listener count exceeds 475, well above the 175 threshold.
  5. The LeakageMonitor detects this and throws a ListenerLeakError, which propagates as an unhandled error to telemetry.

Steps to validate manually

  1. Open VS Code with an agentic chat session that makes many tool calls (50+).
  2. Before the fix, opening the developer console would show warnings about potential listener LEAKs with high listener counts on the configuration emitter.
  3. After the fix, the listener count should be significantly lower since only ONE subscription per rendered chat element handles the show-checkmarks class toggle.

How the fix addresses the issue

Instead of each content part individually subscribing to onDidChangeConfiguration to toggle the show-checkmarks CSS class, a single subscription is added at the row container level (templateData.rowContainer) in the chat list renderer. The existing CSS already uses descendant selectors (.show-checkmarks .progress-container > .codicon.codicon-check), so setting the class on the ancestor container propagates the style to all child parts.

Changed files

  • chatListRenderer.ts: Added a single show-checkmarks class toggle on templateData.rowContainer with one onDidChangeConfiguration subscription per rendered element (via elementDisposables). Also removed the duplicate subscription from renderConfirmationAction.
  • chatToolProgressPart.ts: Removed the per-instance onDidChangeConfiguration subscription and show-checkmarks class toggle from ChatToolProgressSubPart.
  • chatTerminalToolProgressPart.ts: Removed the per-instance onDidChangeConfiguration subscription and show-checkmarks class toggle from the non-collapsible branch of ChatTerminalToolProgressPart. Cleaned up the unused AccessibilityWorkbenchSettingId import.
  • chatMarkdownContentPart.ts: Removed the per-instance onDidChangeConfiguration subscription and show-checkmarks class toggle from CollapsedCodeBlock.
  • chat.css: Updated the .detail.show-checkmarks CSS selector to .interactive-item-container.show-checkmarks .detail so it inherits from the container-level class.

Copilot AI review requested due to automatic review settings March 17, 2026 22:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reduces configuration listener accumulation in the chat renderer by moving per-part ShowChatCheckmarks subscriptions to a single row-container level toggle, addressing listener leak warnings in long/agentic chat sessions.

Changes:

  • Add a single ShowChatCheckmarks class toggle + configuration listener on each chat row container in ChatListItemRenderer.
  • Remove per-instance onDidChangeConfiguration subscriptions for ShowChatCheckmarks from tool progress parts and collapsed code block UI.
  • Clean up an unused AccessibilityWorkbenchSettingId import after removing the per-part subscription.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts Adds row-container show-checkmarks class toggling driven by a single configuration listener per rendered row.
src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatToolProgressPart.ts Removes per-subpart configuration subscription for ShowChatCheckmarks.
src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts Removes per-part configuration subscription for ShowChatCheckmarks (non-collapsible path) and drops unused import.
src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMarkdownContentPart.ts Removes per-instance configuration subscription for ShowChatCheckmarks from CollapsedCodeBlock.

…#301186)

Move the show-checkmarks class toggle from individual content parts to
the row container level in chatListRenderer. This replaces N per-part
onDidChangeConfiguration subscriptions (one per tool invocation, terminal
tool, and code block pill) with a single subscription per rendered chat
element, significantly reducing listener accumulation on the configuration
service emitter in sessions with many tool invocations.
@bryanchen-d bryanchen-d force-pushed the bryanchen-d/fix-listener-leak-show-checkmarks branch from 0665ea5 to 1c11056 Compare March 17, 2026 22:54
@bryanchen-d bryanchen-d marked this pull request as ready for review March 17, 2026 23:12
@vs-code-engineering vs-code-engineering bot added this to the 1.113.0 milestone Mar 17, 2026
@bryanchen-d bryanchen-d requested a review from meganrogge March 17, 2026 23:34
@bryanchen-d bryanchen-d enabled auto-merge March 17, 2026 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Unhandled Error] [044] potential listener LEAK detected, having 475 listeners already. MOST frequent listener (21):

2 participants