Checklist
Summary
When two stdio Serena instances activate the same project, both spawn an Eclipse JDT language server and hand each one the identical -data workspace directory. Eclipse JDT workspaces are single-writer by design, so the two servers continuously invalidate each other's index. The result is that neither ever reaches an idle state: they sat at 160% and 128% CPU with 1.4 GB + 2.6 GB RSS on a single Gradle/Java project, indefinitely, on an otherwise-healthy machine.
Nothing warns the user. There is no lock, no error surfaced through MCP, and no log line indicating the collision — it presents only as the machine becoming slow.
Relationship to existing issues
I found the prior art and want to be explicit about what is and isn't new:
The argument for treating this separately: stdio-per-session is the default, HTTP mode is opt-in, and the failure mode here is silent index corruption plus a multi-GB / multi-core resource leak. A user following the defaults gets no signal that anything is wrong.
Environment
- Serena 1.2.0 (uv tool),
--context=copilot-cli --project-from-cwd
language_backend: LSP
- Ubuntu 24.04.4 LTS, 12-core i7-1365U
- Project: Java/Gradle (
build.gradle, settings.gradle), moderate size
- MCP client: GitHub Copilot app — each session spawns its own stdio Serena instance
Reproduction
- With
language_backend: LSP, open two concurrent MCP sessions whose cwd is the same Java project.
- Each activates the project and starts an Eclipse JDT language server.
- Inspect the
-data argument of both JDTLS processes.
Both receive the same path:
$ tr '\0' '\n' < /proc/625237/cmdline | grep -A1 -x -- -data | tail -1
/home/sp/.serena/language_servers/static/EclipseJDTLS/workspaces/2269b098e07d8bdba1bd239a2fac6177/data_dir
$ tr '\0' '\n' < /proc/626656/cmdline | grep -A1 -x -- -data | tail -1
/home/sp/.serena/language_servers/static/EclipseJDTLS/workspaces/2269b098e07d8bdba1bd239a2fac6177/data_dir
The workspace hash is derived from the project path, so every concurrent session on a given project collides on one workspace directory.
Neither settles. Sampled ~5 minutes after start, with the project otherwise idle:
pid=625237 160.0% CPU 1428 MB
pid=626656 128.2% CPU 2579 MB
Secondary observation: repeated StackOverflowError in the shared workspace
The same workspace's Eclipse log contains 149 StackOverflowError occurrences across 10 rotated .bak_N.log files (Eclipse rotates on restart, so the workspace has been re-initialised at least 10 times). The stack is regex recursion:
java.lang.StackOverflowError
at java.base/java.util.regex.Pattern$Branch.match(Unknown Source)
at java.base/java.util.regex.Pattern$GroupHead.match(Unknown Source)
at java.base/java.util.regex.Pattern$LazyLoop.match(Unknown Source)
at java.base/java.util.regex.Pattern$GroupTail.match(Unknown Source)
at java.base/java.util.regex.Pattern$BranchConn.match(Unknown Source)
at java.base/java.util.regex.Pattern$CharProperty.match(Unknown Source)
This looks like catastrophic backtracking on some input file rather than a concurrency symptom, so it may well be a separate bug — I'm including it because it shares the workspace and may compound the restart churn. Happy to split it into its own issue if you'd prefer.
Suggested fixes
Roughly in order of how well they'd contain the failure:
- Namespace the JDTLS
-data directory per language-server instance, not solely per project path — e.g. append a PID/session/instance suffix. This makes concurrent sessions safe by construction, at the cost of some disk and duplicate indexing.
- Reuse a single language server per project across instances (in-process reference counting), which avoids the duplicate indexing cost entirely and is the semantically ideal fix.
- At minimum, detect and warn. Take an advisory lock on the workspace directory; if it's already held, log loudly and surface an MCP-visible warning rather than proceeding into silent corruption. Even just a startup log line naming the workspace path would make this diagnosable — I only found it by inspecting
/proc/<pid>/cmdline.
Option 3 alone would have turned this from "my machine is mysteriously slow" into a one-line diagnosis.
Workaround for others hitting this
Either switch to the documented single persistent server (HTTP/SSE) mode, or avoid concurrent MCP sessions rooted in the same project. To recover after it has happened, stop the orphaned servers and delete the corrupted workspace so it rebuilds:
rm -rf ~/.serena/language_servers/static/EclipseJDTLS/workspaces/<hash>
Checklist
.serena/project.ymlSummary
When two stdio Serena instances activate the same project, both spawn an Eclipse JDT language server and hand each one the identical
-dataworkspace directory. Eclipse JDT workspaces are single-writer by design, so the two servers continuously invalidate each other's index. The result is that neither ever reaches an idle state: they sat at 160% and 128% CPU with 1.4 GB + 2.6 GB RSS on a single Gradle/Java project, indefinitely, on an otherwise-healthy machine.Nothing warns the user. There is no lock, no error surfaced through MCP, and no log line indicating the collision — it presents only as the machine becoming slow.
Relationship to existing issues
I found the prior art and want to be explicit about what is and isn't new:
jetbrains_launch_commandspawns one IDE per MCP session with no serialisation, so N concurrent sessions race a single IDE cold start and N-1 get a modal DirectoryLock$CannotActivateException #1864 — the same class of problem (N sessions racing a single-instance resource) but for the JetBrains backend andDirectoryLock. This is the LSP/EclipseJDTLS equivalent.init_language_server_managernever completing despite JDTLS finishing; possibly a downstream symptom of the same contention.The argument for treating this separately: stdio-per-session is the default, HTTP mode is opt-in, and the failure mode here is silent index corruption plus a multi-GB / multi-core resource leak. A user following the defaults gets no signal that anything is wrong.
Environment
--context=copilot-cli --project-from-cwdlanguage_backend: LSPbuild.gradle,settings.gradle), moderate sizeReproduction
language_backend: LSP, open two concurrent MCP sessions whose cwd is the same Java project.-dataargument of both JDTLS processes.Both receive the same path:
The workspace hash is derived from the project path, so every concurrent session on a given project collides on one workspace directory.
Neither settles. Sampled ~5 minutes after start, with the project otherwise idle:
Secondary observation: repeated StackOverflowError in the shared workspace
The same workspace's Eclipse log contains 149
StackOverflowErroroccurrences across 10 rotated.bak_N.logfiles (Eclipse rotates on restart, so the workspace has been re-initialised at least 10 times). The stack is regex recursion:This looks like catastrophic backtracking on some input file rather than a concurrency symptom, so it may well be a separate bug — I'm including it because it shares the workspace and may compound the restart churn. Happy to split it into its own issue if you'd prefer.
Suggested fixes
Roughly in order of how well they'd contain the failure:
-datadirectory per language-server instance, not solely per project path — e.g. append a PID/session/instance suffix. This makes concurrent sessions safe by construction, at the cost of some disk and duplicate indexing./proc/<pid>/cmdline.Option 3 alone would have turned this from "my machine is mysteriously slow" into a one-line diagnosis.
Workaround for others hitting this
Either switch to the documented single persistent server (HTTP/SSE) mode, or avoid concurrent MCP sessions rooted in the same project. To recover after it has happened, stop the orphaned servers and delete the corrupted workspace so it rebuilds: