jdtls-lsp Plugin Fails on Windows Due to Invalid File URI Format

Open 💬 17 comments Opened Jan 12, 2026 by WeiminLiang

Summary

The jdtls-lsp plugin fails to initialize on Windows because Claude Code sends Windows file paths with backslashes to the LSP server, resulting in an invalid URI format.

Environment

  • Claude Code Version: 2.1.5
  • OS: Windows 10/11 (MSYS_NT-10.0-26200)
  • Java Version: OpenJDK 21.0.9 (via Scoop)
  • jdtls Version: 1.56.0-202601071638 (via Scoop)
  • Plugin: jdtls-lsp@claude-plugins-official v1.0.0

Steps to Reproduce

  1. Install jdtls via Scoop: scoop install jdtls
  2. Enable jdtls-lsp plugin in .claude/settings.local.json:

``json
{
"enabledPlugins": {
"jdtls-lsp@claude-plugins-official": true
}
}
``

  1. Open a Java/Maven project with Claude Code
  2. Check debug logs for LSP initialization errors

Expected Behavior

The jdtls LSP server should initialize successfully and provide Java code intelligence.

Actual Behavior

LSP initialization fails with the following error:

SEVERE: Internal error: Illegal character in authority at index 9: file://C:\Users\username\path\to\project
java.lang.IllegalArgumentException: Illegal character in authority at index 9: file://C:\Users\username\path\to\project
    at java.base/java.net.URI.create(URI.java:932)
    at org.eclipse.jdt.ls.core.internal.ResourceUtils.canonicalFilePathFromURI(ResourceUtils.java:225)
    at org.eclipse.jdt.ls.core.internal.handlers.BaseInitHandler.handleInitializationOptions(BaseInitHandler.java:109)

Root Cause Analysis

Claude Code is sending the workspace path to jdtls in an invalid URI format:

| Issue | Value |
|-------|-------|
| Sent by Claude Code | file://C:\Users\username\Desktop\project |
| Expected RFC 8089 Format | file:///C:/Users/username/Desktop/project |

The Windows backslashes (\) are being interpreted as invalid characters in the URI authority component.

Additional Issue Found

The marketplace.json configuration for jdtls-lsp and kotlin-lsp plugins includes a startupTimeout field that is not yet implemented:

[ERROR] Failed to initialize LSP server plugin:jdtls-lsp:jdtls:
LSP server 'plugin:jdtls-lsp:jdtls': startupTimeout is not yet implemented.
Remove this field from the configuration.

Location: ~/.claude/plugins/marketplaces/claude-plugins-official/.claude-plugin/marketplace.json

Affected plugins:

  • jdtls-lsp (line ~230): "startupTimeout": 120000
  • kotlin-lsp (line ~189): "startupTimeout": 120000

Suggested Fix

  1. For the URI format issue: Convert Windows paths to proper file URIs before sending to LSP servers:

``javascript
// Example fix
const fileUri =
file:///${windowsPath.replace(/\/g, '/')};
``

  1. For the startupTimeout issue: Remove the startupTimeout field from marketplace.json or implement support for it.

Workaround

For the startupTimeout issue, users can manually edit:
~/.claude/plugins/marketplaces/claude-plugins-official/.claude-plugin/marketplace.json

And remove the "startupTimeout": 120000 lines from jdtls-lsp and kotlin-lsp configurations.

However, there is currently no workaround for the Windows URI format issue.

Debug Logs

Relevant log entries from ~/.claude/debug/*.txt:

2026-01-12T08:16:09.523Z [DEBUG] [LSP PROTOCOL plugin:jdtls-lsp:jdtls] Sending request 'initialize - (0)'.
2026-01-12T08:16:17.577Z [DEBUG] [LSP SERVER plugin:jdtls-lsp:jdtls] SEVERE: Internal error: Illegal character in authority at index 9: file://C:\Users\...
2026-01-12T08:16:17.579Z [DEBUG] [LSP PROTOCOL plugin:jdtls-lsp:jdtls] Received response 'initialize - (0)' in 8055ms. Request failed: Internal error. (-32603).
2026-01-12T08:16:17.579Z [ERROR] Error: Error: LSP server plugin:jdtls-lsp:jdtls initialize failed: Internal error.

Impact

  • All LSP plugins that receive file paths are likely affected on Windows
  • Users cannot use Java code intelligence features via jdtls-lsp
  • Similar issues may affect other LSP plugins (kotlin-lsp, csharp-lsp, etc.)

View original on GitHub ↗

17 Comments

chrisguminski · 6 months ago

Can confirm this issue on Windows 11 with Claude Code 2.1.11

Environment:

  • OS: Windows 11
  • Claude Code: 2.1.11
  • Java: JDK 21.0.9.10 (Eclipse Adoptium)
  • jdtls: Installed via VSCode Java extension (redhat.java-1.51.0-win32-x64)

Error in logs:
LSP error: plugin:jdtls-lsp:jdtls - ENOENT: no such file or directory, uv_spawn 'jdtls'

Observations:

  1. The LSP manager appears to ignore the command field in .lsp.json configurations
  2. Attempted workarounds (.cmd wrapper, absolute paths, python with args) had no effect - the error message remains identical
  3. jdtls executable is in PATH and works when invoked directly from command line

This suggests the LSP manager has two issues on Windows:

  • Not respecting custom command configurations in .lsp.json
  • Not properly resolving executables that require file extensions (.cmd, .bat, .exe)

The URI format issue mentioned in this thread is likely compounding the problem.

privacyguy123 · 5 months ago

+1

I am able to load with it in using this wrapper

jdtls_proxy.py

import sys
import subprocess
import re
import os

# 1. Path to your actual jdtls Python script
# Since your .bat uses %~dp0/jdtls, we target that file
real_jdtls = os.path.join(os.path.dirname(__file__), 'jdtls')

# 2. Launch the real JDTLS process
# We use subprocess.PIPE to intercept the input (stdin)
cmd = [sys.executable, real_jdtls] + sys.argv[1:]
process = subprocess.Popen(
    cmd, 
    stdin=subprocess.PIPE, 
    stdout=sys.stdout, 
    stderr=sys.stderr, 
    bufsize=0
)

try:
    while True:
        # JSON-RPC messages start with a Header (Content-Length: X)
        header = sys.stdin.readline()
        if not header:
            break
        
        if header.startswith("Content-Length:"):
            try:
                length = int(header.split(":")[1])
                # Skip the empty line (\r\n) after the header
                sys.stdin.readline() 
                
                # Read the actual JSON body
                body = sys.stdin.read(length)
                
                # THE FIX: Find "file://C:\" and change to "file:///C:/"
                # Also handles escaped backslashes often sent by Node.js
                new_body = re.sub(r'file://([a-zA-Z]):\\+', r'file:///\1:/', body)
                new_body = new_body.replace('\\\\', '/')
                
                # Recalculate length because adding the '/' and changing chars 
                # might change the byte count
                encoded_body = new_body.encode('utf-8')
                new_length = len(encoded_body)
                
                # Send the fixed message to the real Java server
                process.stdin.write(f"Content-Length: {new_length}\r\n\r\n".encode('utf-8'))
                process.stdin.write(encoded_body)
                process.stdin.flush()
            except Exception as e:
                # If parsing fails, just pass the original through
                process.stdin.write(header.encode('utf-8'))
                process.stdin.write(b"\r\n")
                process.stdin.write(body.encode('utf-8'))
                process.stdin.flush()
        else:
            # Pass through any other lines (like headers we don't care about)
            process.stdin.write(header.encode('utf-8'))
            process.stdin.flush()

except EOFError:
    pass
except KeyboardInterrupt:
    pass
finally:
    process.terminate()

Edit marketplace.json:

    {
      "name": "jdtls-lsp",
      "description": "Java language server (Eclipse JDT.LS) for code intelligence",
      "version": "1.0.0",
      "author": {
        "name": "Anthropic",
        "email": "support@anthropic.com"
      },
      "source": "./plugins/jdtls-lsp",
      "category": "development",
      "strict": false,
      "lspServers": {
        "jdtls": {
          "command": "jdtls.bat",
          "extensionToLanguage": {
            ".java": "java"
          }
        }
      }
    },

and jdtls.bat:

@echo off
python "%~dp0jdtls_proxy.py" %*

Seems like very minor error that could be addressed quickly, but I don't think they are any humans from Anthropic on this whole repo.

There is also a non fatal error about not being able to read "marketplace.json":

2026-01-27T16:31:59.499Z [DEBUG] Loading plugin jdtls-lsp from source: "./plugins/jdtls-lsp"
2026-01-27T16:31:59.500Z [DEBUG] Using provided version for jdtls-lsp@claude-plugins-official: 1.0.0
2026-01-27T16:31:59.504Z [DEBUG] Plugin jdtls-lsp@claude-plugins-official version 1.0.0 already cached at C:\Users\[REDACTED]\.claude\plugins\cache\claude-plugins-official\jdtls-lsp\1.0.0
2026-01-27T16:31:59.537Z [DEBUG] Copied local plugin jdtls-lsp to versioned cache: C:\Users\[REDACTED]\.claude\plugins\cache\claude-plugins-official\jdtls-lsp\1.0.0
2026-01-27T16:31:59.538Z [DEBUG] Plugin jdtls-lsp has no entry.skills defined
2026-01-27T16:31:59.550Z [DEBUG] Checking plugin jdtls-lsp: skillsPath=none, skillsPaths=0 paths
2026-01-27T16:32:02.244Z [DEBUG] Loaded 1 LSP server(s) from plugin: jdtls-lsp
2026-01-27T16:32:02.244Z [DEBUG] Queued request handler for plugin:jdtls-lsp:jdtls.workspace/configuration (connection not ready)
2026-01-27T16:32:02.245Z [DEBUG] Starting LSP server instance: plugin:jdtls-lsp:jdtls
2026-01-27T16:32:02.251Z [DEBUG] Queued notification handler for plugin:jdtls-lsp:jdtls.textDocument/publishDiagnostics (connection not ready)
2026-01-27T16:32:02.251Z [DEBUG] Registered diagnostics handler for plugin:jdtls-lsp:jdtls
2026-01-27T16:32:02.254Z [DEBUG] Applied queued notification handler for plugin:jdtls-lsp:jdtls.textDocument/publishDiagnostics
2026-01-27T16:32:02.254Z [DEBUG] Applied queued request handler for plugin:jdtls-lsp:jdtls.workspace/configuration
2026-01-27T16:32:02.254Z [DEBUG] LSP client started for plugin:jdtls-lsp:jdtls
2026-01-27T16:32:02.255Z [DEBUG] [LSP PROTOCOL plugin:jdtls-lsp:jdtls] Sending request 'initialize - (0)'.
2026-01-27T16:32:02.920Z [DEBUG] [LSP SERVER plugin:jdtls-lsp:jdtls] WARNING: Using incubator modules: jdk.incubator.vector
2026-01-27T16:32:03.830Z [DEBUG] [LSP SERVER plugin:jdtls-lsp:jdtls] Jan 27, 2026 4:32:03 PM org.apache.aries.spifly.BaseActivator log
2026-01-27T16:32:06.516Z [DEBUG] [LSP PROTOCOL plugin:jdtls-lsp:jdtls] Received response 'initialize - (0)' in 4261ms.
2026-01-27T16:32:06.516Z [DEBUG] [LSP PROTOCOL plugin:jdtls-lsp:jdtls] Sending notification 'initialized'.
2026-01-27T16:32:06.516Z [DEBUG] LSP server plugin:jdtls-lsp:jdtls initialized
2026-01-27T16:32:06.516Z [DEBUG] LSP server instance started: plugin:jdtls-lsp:jdtls
2026-01-27T16:32:06.803Z [DEBUG] Using provided version for jdtls-lsp@claude-plugins-official: 1.0.0
2026-01-27T16:32:11.758Z [DEBUG] Failed to read raw marketplace.json: Error: ENOENT: no such file or directory, open 'C:\Users\[REDACTED]\.claude\plugins\cache\claude-plugins-official\jdtls-lsp\.claude-plugin\marketplace.json'

Frankly I don't know who let this into a release buid ... Claude is great, but this is an absolute MESS.

KasporskiDzmitry · 5 months ago

The same for me. jdtls-lsp is not working on Windows.

pdebuitlear · 5 months ago

Do these defects actually get looked at? Also. I don't see the point of releasing functionality that hasn't been tested. These are not edge cases, these are basic smoke test type defects.

chrisguminski · 5 months ago
Do these defects actually get looked at? Also. I don't see the point of releasing functionality that hasn't been tested. These are not edge cases, these are basic smoke test type defects.

Maybe if these this issue gets enough attention and there are many duplicates somehow it bubbles up as something that actually gets worked on. Another issue was closed as a duplicate and linked here by Claude itself. So there is some kind of awareness of it.

pdebuitlear · 4 months ago

Still reproduces as of 2.1.45

privacyguy123 · 4 months ago
> Do these defects actually get looked at? Also. I don't see the point of releasing functionality that hasn't been tested. These are not edge cases, these are basic smoke test type defects. Maybe if these this issue gets enough attention and there are many duplicates somehow it bubbles up as something that actually gets worked on. Another issue was closed as a duplicate and linked here by Claude itself. So there is some kind of awareness of it.

It needs 10 upvotes to "bubble up" - get your friends on the case.

tomSteve25 · 4 months ago

Still happening in 2.1.71

CodeMaven · 4 months ago

I cannot believe how long this has been going on. I also created a proxy, using Java, to convert the URL format, but I cannot share it with the rest of my team that way - the underlying issue needs resolving!

pdebuitlear · 4 months ago

reproduces on v2.1.78

AlexandreSoteras · 3 months ago

Same on 2.1.80 with php-lsp

pdebuitlear · 3 months ago

Still reproducing on v2.1.87

pdebuitlear · 3 months ago

still reproducing in v2.1.97

Pcyslist · 2 months ago

I have a practical solution, and the specific steps are as follows:

  1. Download the latest stable JDTLS compressed package: https://www.eclipse.org/downloads/download.php?file=/jdtls/milestones/1.58.0/jdt-language-server-1.58.0-202604151538.tar.gz
  2. Unzip it to a local directory, for example: D:\Tools\jdtls
  3. Add D:\Tools\jdtls\bin to your PATH environment variable.
  4. Open the ~\.claude\plugins\marketplaces\claude-plugins-official\.claude-plugin\marketplace.json file and search for jdtls.
  5. Modify the found JDTLS LSP configuration information as follows. Note: change "command": "jdtls" to "command": "jdtls.bat":

``json
"lspServers": {
"jdtls": {
"command": "jdtls.bat",
"extensionToLanguage": {
".java": "java"
},
"startupTimeout": 120000
}
}
``

  1. Restart Claude Code, and Claude Code's LSP tool will be able to use JDTLS correctly.
Pcyslist · 2 months ago
I have a practical solution, and the specific steps are as follows: 1. Download the latest stable JDTLS compressed package: https://www.eclipse.org/downloads/download.php?file=/jdtls/milestones/1.58.0/jdt-language-server-1.58.0-202604151538.tar.gz 2. Unzip it to a local directory, for example: D:\Tools\jdtls 3. Add D:\Tools\jdtls\bin to your PATH environment variable. 4. Open the ~\.claude\plugins\marketplaces\claude-plugins-official\.claude-plugin\marketplace.json file and search for jdtls. 5. Modify the found JDTLS LSP configuration information as follows. Note: change "command": "jdtls" to "command": "jdtls.bat": "lspServers": { "jdtls": { "command": "jdtls.bat", "extensionToLanguage": { ".java": "java" }, "startupTimeout": 120000 } } 6. Restart Claude Code, and Claude Code's LSP tool will be able to use JDTLS correctly.

note that: jdtls-1.58.0 required jdk-version is 21 but not 17.

KasporskiDzmitry · 2 months ago

@Pcyslist I was doing the same, but this configuration keeps getting restored to the defaults. It seems like Claude Code pulls marketplace settings every new session or periodically.
I made a hook that overwrites those settings on session startup.
It works.

FMorschel · 1 day ago

6 new issues have been created and linked here by the bots. Why can't you use Claude to fix this?