Skip to content

bash-language-server hardcodes --external-sources causing unbounded ShellCheck memory growth #16209

@marcusquinn

Description

@marcusquinn

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:

  1. There's no way to remove the hardcoded --external-sources flag
  2. There's no OPENCODE_DISABLE_LSP env var to disable LSP entirely (only OPENCODE_DISABLE_LSP_DOWNLOAD which doesn't affect already-installed LSPs)
  3. Headless opencode run sessions 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 run sessions 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

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)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