oraios/serena · error

ShellCheck binary not found at {binary_path} after extractio

Error message

ShellCheck binary not found at {binary_path} after extraction; archive layout may have changed.

What it means

Raised by _install_shellcheck_if_missing after downloading and extracting the ShellCheck release archive: the expected shellcheck binary was not found at its expected path. The library throws this because it downloads a pinned release and assumes a known archive layout; if the binary is missing after extraction, the upstream archive structure likely changed or the extraction silently failed.

Source

Thrown at src/solidlsp/language_servers/bash_language_server.py:181

            install_dir = _shellcheck_install_dir(bash_ls_dir)
            os.makedirs(install_dir, exist_ok=True)

            release = _SHELLCHECK_DEPENDENCIES.get(PlatformUtils.get_platform_id())
            if release is None:
                raise RuntimeError(f"ShellCheck has no upstream binary release for platform {PlatformUtils.get_platform_id().value}")

            archive_type = "zip" if os.name == "nt" else "xztar"
            log.info(f"Downloading ShellCheck v{_SHELLCHECK_VERSION} for {PlatformUtils.get_platform_id().value}")
            FileUtils.download_and_extract_archive_verified(
                release["url"],
                install_dir,
                archive_type,
                expected_sha256=release["sha256"],
                allowed_hosts=_SHELLCHECK_ALLOWED_HOSTS,
            )

            if not os.path.exists(binary_path):
                raise FileNotFoundError(f"ShellCheck binary not found at {binary_path} after extraction; archive layout may have changed.")

            # ensure the binary is executable on POSIX (zip extraction does not preserve perms)
            if os.name != "nt":
                current = os.stat(binary_path).st_mode
                os.chmod(binary_path, current | 0o111)

        def create_launch_command_env(self) -> dict[str, str]:
            bash_language_server_version = self._custom_settings.get("bash_language_server_version", DEFAULT_BASH_LANGUAGE_SERVER_VERSION)
            bash_ls_dir = self._resolve_bash_ls_dir(bash_language_server_version)
            managed_bin_dir = os.path.join(bash_ls_dir, "node_modules", ".bin")
            return {
                "PATH": managed_bin_dir + os.pathsep + os.environ.get("PATH", ""),
                "SHELLCHECK_PATH": _shellcheck_binary_path(bash_ls_dir),
            }

        def _resolve_bash_ls_dir(self, bash_language_server_version: str) -> str:
            # legacy unversioned dir reserved for INITIAL; every other version goes into a versioned subdir
            ls_dirname = (

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Check the extracted directory (ls the install dir under bash-lsp*/shellcheck) to see the actual archive layout and where the binary landed
  2. Clear the managed install directory so a fresh download/extraction is attempted
  3. Pin/report the ShellCheck version: install shellcheck system-wide via package manager so the managed install is skipped (os.path.exists early-returns)
  4. Update the library to a version matching the current upstream ShellCheck archive layout

Example fix

# before (managed install fails)
BashLanguageServer(...)  # raises FileNotFoundError after extraction
// after
# pre-install shellcheck system-wide so the managed install path is skipped
sudo apt-get install shellcheck
BashLanguageServer(...)  # works
Defensive patterns

Strategy: validation

Validate before calling

import os, shutil
if shutil.which("shellcheck") is None and not os.path.exists(os.path.join(ls_resources_dir, "bash-lsp", "shellcheck", "shellcheck")):
    print("shellcheck will be managed-installed; if it fails, pre-install shellcheck system-wide")

Try / catch

try:
    BashLanguageServer(...)
except FileNotFoundError as e:
    if "ShellCheck binary not found" in str(e):
        # fall back to system shellcheck or clear the install dir and retry
        shutil.which("shellcheck") or install_shellcheck_via_pkg_manager()
    else:
        raise

Prevention

When it happens

Trigger: Calling BashLanguageServer startup (_get_or_install_core_dependency -> _install_shellcheck_if_missing) when shellcheck is not already present, the pinned release URL/SHA is downloaded and extracted successfully, but _shellcheck_binary_path(bash_ls_dir) does not exist afterwards (e.g. archive now nests the binary in a different subdirectory, partial extraction, or disk issue).

Common situations: Upstream ShellCheck GitHub release changes its archive layout (renames/renests the binary); corrupted or truncated download that extracts but omits the binary; a proxy/mirror serving a different archive; filesystem errors during extraction; a stale pinned version in the library.

Related errors


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