oraios/serena · error · SolidLSPException

java_home='{explicit_home}' is invalid: '{candidate}' does n

Error message

java_home='{explicit_home}' is invalid: '{candidate}' does not exist. Set ls_specific_settings.java.java_home to a JDK home that contains bin/{java_exe_name}.

What it means

SolidLSPException thrown by _resolve_system_jdk when the explicit ls_specific_settings.java.java_home setting points to a directory that does not contain bin/<java executable>. The library validates the candidate JDK before using it so JDTLS never starts with a broken java path.

Source

Thrown at src/solidlsp/language_servers/eclipse_jdtls.py:588

            The ``java`` executable is located by priority: ``java_home`` setting ->
            ``JAVA_HOME`` env var -> ``java`` in PATH. The actual JDK home directory and
            major version are then discovered by querying the JVM itself via
            ``java -XshowSettings:properties -version`` — this is the single source of truth
            and works correctly even when the locator is a system stub (e.g. ``/usr/bin/java``
            on macOS, which delegates to ``/usr/libexec/java_home`` under the hood and does not
            resolve to the real JDK home via simple path traversal).

            :return: (jdk_home_directory, java_executable_path)
            """
            # locate a java executable to interrogate
            java_exe_name = "java.exe" if platform.system() == "Windows" else "java"
            java_exe: str | None = None
            source: str

            if explicit_home := custom_settings.get("java_home"):
                candidate = str(Path(explicit_home) / "bin" / java_exe_name)
                if not os.path.exists(candidate):
                    raise SolidLSPException(
                        f"java_home='{explicit_home}' is invalid: '{candidate}' does not exist. "
                        f"Set ls_specific_settings.java.java_home to a JDK home that contains bin/{java_exe_name}."
                    )
                java_exe = candidate
                source = f"java_home setting ({explicit_home})"
            elif env_home := os.environ.get("JAVA_HOME"):
                candidate = str(Path(env_home) / "bin" / java_exe_name)
                if os.path.exists(candidate):
                    java_exe = candidate
                    source = f"JAVA_HOME env ({env_home})"
                else:
                    log.warning(f"JAVA_HOME='{env_home}' invalid (no '{candidate}'), falling back to PATH.")

            if java_exe is None:
                java_in_path = shutil.which("java")
                if java_in_path is None:
                    raise SolidLSPException(
                        "Could not locate a Java installation for JDTLS. "

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Set ls_specific_settings.java.java_home to a real JDK home that contains bin/java (e.g. /usr/lib/jvm/temurin-21-jdk-amd64)
  2. Verify with: ls <java_home>/bin/java (or bin/java.exe on Windows)
  3. Remove the java_home setting to fall back to JAVA_HOME or PATH
  4. Update the path if the JDK was upgraded/moved

Example fix

// before
ls_specific_settings:
  java:
    java_home: /usr/lib/jvm/java-21  # does not exist
// after
ls_specific_settings:
  java:
    java_home: /usr/lib/jvm/temurin-21-jdk-amd64  # contains bin/java
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
import os
def validate_java_home(java_home: str, exe: str = "java") -> bool:
    return os.path.exists(Path(java_home) / "bin" / exe)

Type guard

def is_valid_jdk_home(p: object) -> bool:
    return isinstance(p, (str, Path)) and (Path(p) / "bin" / ("java.exe" if os.name == "nt" else "java")).exists()

Try / catch

try:
    ls = start_jdtls(settings)
except SolidLSPException as e:
    if str(e).startswith("java_home="):
        settings.pop("java_home", None)  # fall back to JAVA_HOME/PATH and retry
        ls = start_jdtls(settings)
    else:
        raise

Prevention

When it happens

Trigger: Setting ls_specific_settings.java.java_home to a path where Path(home)/bin/java does not exist; _setup_from_existing_install calls _resolve_system_jdk which checks os.path.exists(candidate) and raises immediately.

Common situations: Typo in java_home path; pointing java_home at the JDK's parent directory instead of the JDK home; pointing at a JRE-less runtime image without bin/java; path changed after a JDK upgrade; Windows users giving the java.exe file path instead of the home directory.

Related errors


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/3efb258a71a6b61b. Report an issue: GitHub.