Skip to content

Memory leak: LSP client diagnostics and files maps not cleared on shutdown #9143

Closed

Description

@sauerdaniel

Problem

When an LSP client shuts down, the diagnostics Map and files object are not cleared in the shutdown() method of LSPClient. This can cause:

  1. Memory retention - Diagnostics data for closed files remains in memory
  2. Stale data - If the same LSP server is reconnected, old diagnostics may persist
  3. Memory growth - Over multiple LSP reconnections, maps accumulate entries

Code Location

packages/opencode/src/lsp/client.ts - The shutdown() method closes the connection but doesn't clear internal state.

Proposed Solution

Add cleanup calls in the shutdown() method:

diagnostics.clear()
for (const path in files) delete files[path]

Impact

  • Minimal change (2 lines)
  • Ensures clean state on LSP shutdown
  • Prevents memory growth from accumulated diagnostics

Relates to #5363

Activity

  1. added
    perfIndicates a performance issue or need for optimization
    on Jan 17, 2026
  2. github-actions commented on Jan 17, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of or closely related to existing memory management issues. Please check:

    These issues all address memory retention and cleanup problems in OpenCode, including LSP-related memory leaks. Your proposed fix for clearing diagnostics and files maps in LSPClient.shutdown() is directly addressing part of the root cause identified in #9140 regarding unbounded caches.

    Feel free to ignore if your specific case differs from these.

  3. changed the title [-]LSP client diagnostics and files maps not cleared on shutdown[/-] [+]Memory leak: LSP client diagnostics and files maps not cleared on shutdown[/+] on Jan 18, 2026
  4. aptor9 commented on Jan 19, 2026

    @aptor9

    This is what I got from claude. Maybe worth a double check?

    Do I need garbage collect diagnostics or paths variables in lsp/client.ts?

    ● Looking at this code, there are two data structures that accumulate state:

    1. diagnostics (line 51) - Map<string, Diagnostic[]> - Stores diagnostics per file path
    2. files (line 135-137) - { [path: string]: number } - Tracks document versions

    Short answer: No, you likely don't need to.

    Here's why:

    1. Lifecycle is tied to the LSP client - Both maps are naturally garbage collected when the client calls shutdown() (line 239-245) and the client object is released.
    2. diagnostics is self-updating - When the LSP server clears diagnostics for a file, it sends an empty array via publishDiagnostics, which updates the map correctly. It's also
      cleared on file open (line 194).
    3. files versioning is needed - The version numbers must persist to correctly distinguish between didOpen vs didChange notifications. Clearing this could cause protocol issues
      if a file is re-opened (version conflicts).
    4. Memory impact is minimal - These store string keys with small values (arrays or numbers). Even with hundreds of files, the memory footprint is negligible.

    When you might want to consider it:

    • If the LSP client lives for extremely long sessions with thousands of unique files being touched
    • If you implement a textDocument/didClose notification (currently missing), you could clean up both maps when a file is explicitly closed

    For typical usage patterns, the current design is fine.

  5. sauerdaniel commented on Jan 19, 2026

    @sauerdaniel
    ContributorAuthor

    This was already fixed in commit f936043dd (Jan 15, 2026):

    diagnostics.clear()
    for (const path in files) delete files[path]

    The fix is exactly as proposed in this issue. See: f936043

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

Metadata

Metadata

Assignees

Labels

perfIndicates a performance issue or need for optimization

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions