-
Notifications
You must be signed in to change notification settings - Fork 11.9k
Description
Summary
The bash-language-server bundled with OpenCode hardcodes --external-sources in every ShellCheck invocation. On projects with many shell scripts that source each other (e.g. 100+ scripts with source-path=SCRIPTDIR in .shellcheckrc), this causes exponential AST expansion — individual shellcheck processes grow to 3-5 GB RSS and never terminate.
With multiple concurrent headless sessions (opencode run), each spawning its own bash-language-server, this compounds rapidly. Today we observed 73 concurrent shellcheck processes consuming 17.8 GB RAM on a 64 GB machine.
Root Cause
In bash-language-server/out/config.js line 24:
// Additional ShellCheck arguments. Note that we already add the following arguments: --shell, --format, --external-sources.
The --external-sources flag is hardcoded and cannot be disabled. When combined with a project's .shellcheckrc containing source-path=SCRIPTDIR, ShellCheck follows all source directives recursively across every script in the project, causing exponential memory growth.
The bash-language-server does support SHELLCHECK_PATH and SHELLCHECK_ARGUMENTS env vars for configuration, but:
- There's no way to remove the hardcoded
--external-sourcesflag - There's no
OPENCODE_DISABLE_LSPenv var to disable LSP entirely (onlyOPENCODE_DISABLE_LSP_DOWNLOADwhich doesn't affect already-installed LSPs) - Headless
opencode runsessions don't need LSP diagnostics at all, but there's no way to disable them
Environment
- OpenCode 1.2.17, macOS, Apple Silicon, 64 GB RAM
- Project: ~460 shell scripts with cross-sourcing via
source-path=SCRIPTDIR - Reproduction: run 2+ concurrent
opencode runsessions on a shell-heavy project
Suggested Fixes (any of these would help)
1. Add OPENCODE_DISABLE_LSP=true env var (simplest, highest impact)
Disable all LSP servers when set. Headless opencode run sessions don't benefit from LSP diagnostics — the LLM doesn't use them. This would eliminate the entire class of LSP memory issues for automated/CI use cases.
2. Don't hardcode --external-sources in bash-language-server invocation
Let users control this via SHELLCHECK_ARGUMENTS or a config option. The --external-sources flag is the direct cause of the exponential expansion. Without it, ShellCheck completes in milliseconds.
3. Add per-process memory/time limits to LSP child processes
ShellCheck processes spawned by the language server should have a timeout (e.g. 30s) and memory limit. A single shellcheck invocation should never run for 35+ minutes or consume 5+ GB.
4. Share LSP instances across sessions (related: #13041)
Multiple opencode run sessions on the same project each spawn their own bash-language-server + shellcheck processes. A shared LSP daemon would reduce the multiplier.
Current Workaround
We use a shellcheck-wrapper.sh that strips --external-sources from arguments before passing to the real shellcheck binary, configured via SHELLCHECK_PATH env var. This works but requires manual setup and the env var must be set in .zshenv (not .zshrc) to affect non-interactive shells.
Related Issues
- opencode eating 70gb of memory? #5363 — opencode eating 70gb of memory (36 comments, same root cause pattern)
- Per-session MCP server and LSP duplication multiplies memory usage across concurrent sessions #13041 — Per-session MCP/LSP duplication multiplies memory
- Memory leak, LSP spam, 1 Frame per minute #13796 — Memory leak, LSP spam
- Disabling LSP for specific file extensions #4957 — Disabling LSP for specific file extensions
- [FEATURE]: Deactivate unused LSP servers during a session #12976 — Deactivate unused LSP servers
- [FEATURE]: TUI whole and LSP memory limits as part of configuration #8947 — TUI/LSP memory limits config