#2045·serena

Dart language server burns ~1 CPU core continuously at idle when activated on a monorepo root (rootUri hardcoded to project root; no idle recycling)

Author: uhikaCreated Sep 16, 2026Updated Sep 17, 2026

Environment

  • Serena 1.7.0 (stdio MCP, LSP backend)
  • Bundled DartLanguageServer, Dart SDK 3.7.1
  • macOS arm64

Symptom

On a large monorepo root (several sub-projects in different languages: Vue/TS, Java, C++, and one Flutter/Dart sub-project), the dart language-server process burns ~1 CPU core continuously for hours with zero requests issued. Measured: 40–48 CPU-seconds per 60s wall-clock at idle, %CPU fluctuating 120–150%, RSS 350–490 MB (peak ~1.1 GB).

All other language servers in the same setup (vue/ts/java/cpp/clangd) consume ~0 CPU at idle (0.03–0.05s per 60s). So the Dart server is a clear outlier.

Note: activating the same Dart SDK via a client that roots it at the real Flutter sub-project directory (which has pubspec.yaml) shows no such behavior — it converges and idles.

Steps to reproduce

  1. Monorepo root with: no pubspec.yaml at root; a few hundred .dart files scattered under one sub-project that does have pubspec.yaml; hundreds of thousands of files in total (incl. node_modules, build dirs).
  2. project.yml: language_servers: [vue, typescript_vts, java, cpp, dart].
  3. Start serena MCP with --project-from-cwd at the monorepo root.
  4. Wait a few minutes without issuing any request; the dart language-server process stays at high CPU.

Root cause (confirmed against SDK/source)

  • solidlsp unconditionally sets rootUri = project root in the initialize request (DefaultInitializeParamsBuilder._apply_updates), plus workspaceFolders from ls_workspace_folders.
  • The Dart analysis server adds both workspaceFolders and rootUri to its analysis roots (no dedup). So the entire monorepo root becomes an analysis root regardless of ls_workspace_folders.
  • The analysis server then recursively enumerates the whole tree, installs a recursive FSEvents watcher on it, and background-analyzes every .dart file. Sub-projects without pubspec.yaml fall back to BasicWorkspace (no package resolution), so package: imports fail and each file is analyzed in isolation — pure waste.
  • Active builds in the monorepo continuously generate FS events that keep the watcher busy, so CPU never settles.
  • Serena starts all configured language servers eagerly and has no idle timeout / lazy start / recycling until the MCP session ends.

Expected behavior

  • rootUri should not be unconditionally set to the project root when explicit workspaceFolders are provided (or should be deduped so a folder is not both rootUri and a workspace folder).
  • Ideally, support per-language workspace folders, e.g. allow each entry in language_servers to carry a rootDir, so the Dart server can be rooted at the actual Flutter sub-project.
  • Consider idle-based recycling of language servers.

Suggested fixes (in order of preference)

  1. Per-language rootDir in language_servers (most general; benefits any language, not just Dart).
  2. Don't set rootUri to the project root when workspaceFolders are present; or dedupe rootUri against workspaceFolders.
  3. Idle timeout that stops an inactive language server and re-spawns on demand.

Workaround (what we do now)

Remove dart from language_servers and rely on another tool for Dart symbol queries. (The deprecated onlyAnalyzeProjectsWithOpenFiles: true also reduces CPU but is marked deprecated and hurts workspace symbol search.)