Skip to content

Fix ruby-lsp startup failures: rbenv support and Bundler isolation#1094

Open
EarthmanWeb wants to merge 4 commits intooraios:mainfrom
EarthmanWeb:fix/ruby-lsp-rbenv
Open

Fix ruby-lsp startup failures: rbenv support and Bundler isolation#1094
EarthmanWeb wants to merge 4 commits intooraios:mainfrom
EarthmanWeb:fix/ruby-lsp-rbenv

Conversation

@EarthmanWeb
Copy link
Contributor

Problem

ruby-lsp fails to start in two common setups:

1. rbenv-managed Ruby
When rbenv is active, ruby-lsp is launched via the shim path (e.g.
~/.rbenv/shims/ruby-lsp). Shims route through rbenv at runtime, but
the gem environment they resolve to can differ from the Ruby version
specified in .ruby-version. This causes ruby-lsp to run under the
wrong Ruby version or fail to find gems entirely.

2. Projects with native gem dependencies (e.g. mysql2, pg)
Ruby's Bundler auto-loads the project's Gemfile at startup via
bundler/setup.rb. If any gem in the project requires native extensions
that aren't compiled (e.g. mysql2 without libmysqlclient), the
Ruby process fails before ruby-lsp even initialises. This affects any
project using database gems.

Fix

Three targeted changes, all in ruby_lsp.py:

1. rbenv: launch via rbenv exec
When use_rbenv is detected, ruby-lsp is launched as
["rbenv", "exec", "ruby-lsp"] instead of via the shim path. This
ensures the correct Ruby version and gem environment are always used.
Same fix applied to gem install when ruby-lsp is not yet installed.

2. BUNDLE_GEMFILE isolation
Sets BUNDLE_GEMFILE=.ruby-lsp/Gemfile in the process environment
before launching ruby-lsp. This prevents Bundler from auto-loading
the project's Gemfile, isolating ruby-lsp from native project gems.

3. Minimal composed bundle
Pre-creates .ruby-lsp/Gemfile (with only ruby-lsp + rbs < 3.9
for Ruby < 3.4 compatibility) and the bundle_is_composed marker file
so ruby-lsp skips its own bundle setup entirely. Stale Gemfile.lock
and main_lockfile_hash from failed previous attempts are cleaned up.

Example

Before this fix, opening a Rails project with gem "mysql2" in its
Gemfile would produce:

Could not find gem 'mysql2' in locally installed gems.
Run `bundle install` to install missing gems.

...and ruby-lsp would never start.

After this fix, ruby-lsp starts cleanly because Bundler never sees
the project's Gemfile.

When rbenv is active, ruby-lsp and gem install must be invoked via
"rbenv exec" to ensure the correct Ruby version and gem environment
are used. Without this, the bare shim path can resolve to a different
Ruby version than the one specified in .ruby-version, causing ruby-lsp
to be installed into or launched from the wrong gem environment.
Three fixes for Ruby LS failing to start in projects with native deps:

1. Set BUNDLE_GEMFILE env var pointing to .ruby-lsp/Gemfile so Bundler
   doesn't auto-load the project's Gemfile at Ruby startup. This is the
   root fix — without it, bundler/setup.rb materializes ALL project gems
   (e.g. mysql2) before ruby-lsp starts.

2. Write a minimal .ruby-lsp/Gemfile containing only ruby-lsp gem.

3. Create bundle_is_composed marker so ruby-lsp skips its own composed
   bundle setup.
rbs >= 3.9 uses bare Pathname() which is only available in Ruby 3.4+.
When ruby-lsp pulls in rbs 3.10.x on Ruby 3.3.6, it crashes with
NoMethodError: undefined method 'Pathname' for class RBS::EnvironmentLoader.
@ddarbyson
Copy link

ddarbyson commented Mar 4, 2026

🎉

Copy link
Contributor

@MischaPanch MischaPanch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't say I understand what's going on here and the implementation is becoming even more unmaintainable, with all the branching logic and the various scenarios and returns. But if you say it improves the status quo for ruby, I'm going to trust it. Just a minor question to address

@kiakiraki FYI - if you have time, I would appreciate your input on this

# Create a minimal .ruby-lsp bundle environment to isolate ruby-lsp
# from the project's Gemfile. Without this, Bundler auto-loads the
# project's gems at Ruby startup (via bundler/setup.rb), failing if
# any gem has unmet native dependencies (e.g. mysql2).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it a problem if it fails? Can't we assume that all native dependencies were installed?

Copy link
Contributor Author

@EarthmanWeb EarthmanWeb Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MischaPanch

When a maintainer inherits a cloned repo where the .serena/project.yml config is stored and shared with the repo, but the maintainer is not actively working on the ruby parts of the project (in my case) then the unmet ruby requirements cause the LSP servers to ALL fail, because of these unmet dep's.

In my other PR, #1095 I suggested that a single failure should not kill all serena language servers - If that was adopted then this might be a moot point, unless in fact it also causes a problem if the dep's ARE all installed locally.

In any case, dep's installed or not, seems like extra overhead for it to fire up the sequence of checking all the references are accurate, simply to allow for LSP reading/editing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerning your actual problem - we are soon (probably today or tomorrow) merging a solution for that. You will be able to add project.local.yml which is your personal config and not checked in. It can override the settings in the shared project.yml, so you can configure your own language list, excluding ruby. @ddarbyson this is exactly what you want, right?

As to this block - it makes sense to support this, but it shouldn't be the default and it should be configurable through ls_specific_options. There one could add a flag use_standalone_rbenv_bundle which by default is false. If you want to do that, feel free to extend the PR and the corresponding documentation. Otherwise, you can also just delete this blog.

As @opcode81 and I mentioned before, while silently failing to start an LS might be suitable behavior for you, for many users it's entirely unacceptable to silently not have their languages configuration respected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MischaPanch
Thanks for the feedback

RE: "but it shouldn't be the default "

Why would you say that? 
Isn't decreased overload (loading the entire Ruby setup) for startup a benefit? 
Would it break something else, if it's used by default?
Does it require the project's full Ruby env loaded, just to use lsp on the files?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RE: "we are soon (probably today or tomorrow) merging a solution for that. You will be able to add project.local.yml which is your personal config and not checked in. It can override the settings in the shared project.yml, so you can configure your own language list, excluding ruby."

Awesome! Great work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it break something else, if it's used by default?

I honestly don't know, that's why I'm cautious. Maybe it's fine, or maybe some LSP functionality gets lost this way. Since I can't test, I'm hesitant to make a change that will affect ruby users. If you know for sure that it's fine, we can keep it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it makes our code more complex ;)

@ddarbyson
Copy link

Those Ruby guys eh?

Don't want to hi-jack the topic. I get @EarthmanWeb scenario.

It would be nice if Serena could use similar settings.local.json conventions to allow every developer on the project to commit their Serena files and memories without polluting other developers user space. If I'm reading Earthman's pull request properly, I think this is the problem he's trying to solve.

Last time I looked that wasn't available so we removed Serena from the stack.

@opcode81
Copy link
Contributor

opcode81 commented Mar 5, 2026

@ddarbyson support for project.local.yml has been added.

@EarthmanWeb please let us know whether you think the additions in this PR are still relevant. As a non-Ruby user, I cannot evaluate this.

@kiakiraki
Copy link
Contributor

Disclaimer: I'm not a maintainer of this project — just a third-party observer sharing my perspective.

  1. rbenv exec fix — worth merging (as a separate PR)
    The current code detects use_rbenv and uses rbenv exec for ruby_cmd / bundle_cmd, but the ruby-lsp launch and gem install paths don't use it. This is an inconsistency that would affect any rbenv user who actually wants to use Ruby LSP.

  2. Bundler isolation — should be opt-in, not default
    ruby-lsp reads the project's Gemfile intentionally to provide project-aware completions and type information. Isolating
    it by default could degrade LSP functionality for users whose environments work fine. Making this a flag in ls_specific_options (as @MischaPanch suggested) seems like the right approach.
    Also worth noting: this PR manually creates .ruby-lsp/Gemfile and the bundle_is_composed marker, effectively overriding
    ruby-lsp's own internal bundle management. This could break on future ruby-lsp updates.

  3. The original motivation is already resolved
    The scenario that prompted this PR (Ruby LSP failing and taking down all other language servers) is addressed by
    project.local.yml, which lets users exclude languages they don't need.

@opcode81
Copy link
Contributor

@EarthmanWeb will you implement @kiakiraki's suggestions?

@EarthmanWeb
Copy link
Contributor Author

EarthmanWeb commented Mar 15, 2026 via email

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants