Skip to content

URI::InvalidURIError crash from VS Code extensions using custom URI schemes (e.g. Claude Code) #4067

@zerocool4u2

Description

@zerocool4u2

Description

Description

Hi, I'm having trouble with Claude Code when using VS Code as my IDE with the ruby-lsp extension. Every time Claude suggests a code change (which opens an inline diff view), it crashes my ruby-lsp server. Here's the general problem:

VS Code extensions that register custom document URI schemes can crash the ruby-lsp server. The Claude Code extension uses _claude_fs_right: and _claude_fs_left: schemes for inline diff views. These have a leading underscore, which is invalid per RFC 3986 §3.1 (schemes must start with a letter).

The crash occurs in two code paths:

  1. Main thread (base_server.rb:51): URI(uri) raises URI::InvalidURIError, but the begin/rescue only catches Store::NonExistingDocumentError. The unrescued exception kills the server (exit code 1).

  2. Worker thread (server.rb run_combined_requests): Feature handlers call URI() independently, crashing the worker thread.

Once the server dies, all requests fail with "Connection is disposed."

Ruby LSP Information

VS Code Version

1.116.0

Ruby LSP Extension Version

0.8.12

Ruby LSP Server Version

0.26.9

Ruby LSP Addons

Ruby Version

3.4.8

Ruby Version Manager

rvm

Installed Extensions

Click to expand
  • Bookmarks (14.1.1)
  • EditorConfig (0.18.2)
  • LogFileHighlighter (3.5.1)
  • better-comments-2 (2.1.0)
  • claude-code (2.1.109)
  • code-spell-checker (4.5.6)
  • codesnap (1.3.4)
  • debugpy (2025.18.0)
  • dotenv (1.0.1)
  • elixir-ls (0.30.0)
  • git-graph (1.30.0)
  • gitignore (0.10.0)
  • gitlens (17.12.0)
  • go (0.52.2)
  • haml (1.4.1)
  • headwind (1.7.0)
  • latex-support (3.10.0)
  • latex-workshop (10.14.1)
  • markfiles (1.1.4)
  • phoenix (0.1.3)
  • pinboard (0.0.28)
  • prettier-vscode (12.4.0)
  • python (2026.4.0)
  • rails-routes (0.6.3)
  • rails-snippets (1.0.8)
  • remote-containers (0.447.0)
  • ruby-lsp (0.8.12)
  • solargraph (0.25.0)
  • sql-formatter-vsc (4.2.5)
  • svg (1.5.4)
  • transpose (0.0.6)
  • vscode-better-align (1.4.5)
  • vscode-containers (2.4.1)
  • vscode-docker (2.0.0)
  • vscode-erb-beautify (0.5.0)
  • vscode-eslint (3.0.24)
  • vscode-github-actions (0.31.3)
  • vscode-kubernetes-tools (1.3.29)
  • vscode-latex (2.0.0)
  • vscode-markdownlint (0.61.2)
  • vscode-postcss-sorting (3.0.1)
  • vscode-pylance (2026.2.1)
  • vscode-settings-cycler (1.0.1)
  • vscode-sort-json (1.20.0)
  • vscode-tailwindcss (0.14.29)
  • vscode-yaml (1.21.0)
  • vscode-yaml-sort (6.6.4)

Ruby LSP Settings

Click to expand
Workspace
{}
User
{
  "enableExperimentalFeatures": false,
  "enabledFeatures": {
    "codeActions": true,
    "diagnostics": true,
    "documentHighlights": true,
    "documentLink": true,
    "documentSymbols": true,
    "foldingRanges": true,
    "formatting": true,
    "hover": true,
    "inlayHint": true,
    "onTypeFormatting": true,
    "selectionRanges": true,
    "semanticHighlighting": true,
    "completion": true,
    "codeLens": true,
    "definition": true,
    "workspaceSymbol": true,
    "signatureHelp": true,
    "typeHierarchy": true
  },
  "featuresConfiguration": {},
  "addonSettings": {},
  "rubyVersionManager": {
    "identifier": "rvm"
  },
  "customRubyCommand": "",
  "formatter": "rubocop",
  "linters": null,
  "bundleGemfile": "",
  "testTimeout": 30,
  "branch": "",
  "pullDiagnosticsOn": "both",
  "useBundlerCompose": false,
  "bypassTypechecker": false,
  "rubyExecutablePath": "",
  "indexing": {},
  "erbSupport": true,
  "useLauncher": false
}

Reproduction steps

  1. Have a Ruby project with ruby-lsp in the Gemfile
  2. Open the project in VS Code with the Ruby LSP extension active
  3. Install the Claude Code VS Code extension, or start claude in an external terminal and run /ide to connect it to VS Code
  4. Ask Claude to make a code change to any Ruby file — it opens an inline diff view using _claude_fs_right: / _claude_fs_left: URI schemes
  5. Ruby LSP server crashes with exit code 1
  6. All LSP features stop working with "Connection is disposed" errors

Code snippet or error message

# Main thread crash (kills the server):
/gems/ruby-lsp-0.26.9/lib/ruby_lsp/base_server.rb:51:in 'block (2 levels) in RubyLsp::BaseServer#start'
URI::RFC3986_Parser#split: bad URI (is not URI?): "_claude_fs_right:/path/to/file.rb" (URI::InvalidURIError)
    from uri-1.1.1/lib/uri/rfc3986_parser.rb:130:in 'URI::RFC3986_Parser#split'
    from uri-1.1.1/lib/uri/common.rb:915:in 'Kernel#URI'
    from ruby-lsp-0.26.9/lib/ruby_lsp/base_server.rb:51
# Worker thread crash (separate code path):
URI::RFC3986_Parser#split: bad URI (is not URI?): "_claude_fs_right:/path/to/file.rb" (URI::InvalidURIError)
    from ruby-lsp-0.26.9/lib/ruby_lsp/server.rb:449:in 'RubyLsp::Server#run_combined_requests'
    from ruby-lsp-0.26.9/lib/ruby_lsp/server.rb:32:in 'RubyLsp::Server#process_message'
    from ruby-lsp-0.26.9/lib/ruby_lsp/base_server.rb:181:in 'RubyLsp::BaseServer#handle_incoming_message'

Suggested Fix

Add rescue URI::InvalidURIError to the existing begin/rescue in base_server.rb:

# base_server.rb - inside the begin/rescue block in #start
+           rescue URI::InvalidURIError
+             # Skip URIs with unsupported schemes (e.g. custom VS Code document providers)
            rescue Store::NonExistingDocumentError
              # If we receive a request for a file that no longer exists, we don't want to fail

And guard process_message in server.rb to reject unparseable URIs early:

# server.rb
  def process_message(message)
+   uri = message.dig(:params, :textDocument, :uri)
+   if uri.is_a?(String)
+     begin
+       URI(uri)
+     rescue URI::InvalidURIError
+       id = message[:id]
+       send_message(Result.new(id: id, response: nil)) if id
+       return
+     end
+   end
+
    case message[:method]

Workaround

A project-local addon that patches Kernel#URI and Server#process_message at runtime. Place at ruby_lsp/claude_code_uri_fix/addon.rb:

Click to expand
# frozen_string_literal: true

module RubyLsp
  module ClaudeCodeUriFix
    module KernelUriPatch
      def URI(uri)
        super
      rescue ::URI::InvalidURIError
        raise unless uri.is_a?(String) && uri.start_with?("_claude_fs_")
        raise ::RubyLsp::Store::NonExistingDocumentError, uri
      end
    end

    module ServerPatch
      def process_message(message)
        uri = message.dig(:params, :textDocument, :uri)
        if uri.is_a?(String) && uri.start_with?("_claude_fs_")
          id = message[:id]
          send_message(Result.new(id: id, response: nil)) if id
          return
        end
        super
      end
    end

    class Addon < ::RubyLsp::Addon
      def activate(_global_state, _outgoing_queue)
        ::Kernel.prepend(KernelUriPatch)
        ::RubyLsp::Server.prepend(ServerPatch)
      end

      def deactivate; end
      def name = "Claude Code URI Fix"
      def version = "1.0.0"
    end
  end
end

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions