pypa/pip · error · InstallationError

Directory is not installable. Neither 'setup.py' nor…

Error message

Directory {name!r} is not installable. Neither 'setup.py' nor 'pyproject.toml' found.

What it means

pip was given a local directory path to install, but the directory does not contain either 'setup.py' or 'pyproject.toml'. The function _get_url_from_path() checks is_installable_dir() and, when it fails, reports that the directory is not a valid installable Python project.

Solutions

  1. Verify the target directory contains setup.py or pyproject.toml: 'ls ./some-dir/setup.py ./some-dir/pyproject.toml'
  2. Navigate to or point at the correct project root directory that has the build files
  3. Add a minimal pyproject.toml to the directory if it is meant to be installable
  4. Check for typos in the path and ensure relative paths are resolved from the correct working directory

Example fix

# before
pip install ./src

# after
pip install ./src/mypackage   # the dir that actually has pyproject.toml
Defensive patterns

Strategy: validation

Validate before calling

import os

def is_installable(path: str) -> bool:
    if not os.path.isdir(path):
        return False
    return os.path.exists(os.path.join(path, "setup.py")) or os.path.exists(
        os.path.join(path, "pyproject.toml")
    )

if not is_installable("./some-dir"):
    print("Directory is not installable; add setup.py or pyproject.toml")

Type guard

def is_installable_dir(path: str) -> bool:
    """Narrowing guard: True if path is a dir with setup.py or pyproject.toml."""
    import os
    return (
        os.path.isdir(path)
        and (
            os.path.isfile(os.path.join(path, "setup.py"))
            or os.path.isfile(os.path.join(path, "pyproject.toml"))
        )
    )

Try / catch

from pip._internal.exceptions import InstallationError
from pip._internal.utils.misc import is_installable_dir

if not is_installable_dir(path):
    raise SystemExit(f"{path} is not an installable directory")

Prevention

When it happens

Trigger: Running 'pip install ./some-dir/' where some-dir exists as a directory but has no setup.py or pyproject.toml. Also triggered when a requirements file points at a directory path that is a subfolder of the actual project root.

Common situations: Pointing at the wrong subdirectory in a monorepo (e.g. ./src instead of ./src/mypackage). Checking out only part of a repo. A directory that contains source code but was never set up as a package. CI running from the wrong working directory.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/8ea6b09fd2b67472. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_internal/req/constructors.py:315

        return True
    return False


def _get_url_from_path(path: str, name: str) -> str | None:
    """
    First, it checks whether a provided path is an installable directory. If it
    is, returns the path.

    If false, check if the path is an archive file (such as a .whl).
    The function checks if the path is a file. If false, if the path has
    an @, it will treat it as a PEP 440 URL requirement and return the path.
    """
    if _looks_like_path(name) and os.path.isdir(path):
        if is_installable_dir(path):
            return path_to_url(path)
        # TODO: The is_installable_dir test here might not be necessary
        #       now that it is done in load_pyproject_toml too.
        raise InstallationError(
            f"Directory {name!r} is not installable. Neither 'setup.py' "
            "nor 'pyproject.toml' found."
        )
    if not is_archive_file(path):
        return None
    if os.path.isfile(path):
        return path_to_url(path)
    urlreq_parts = name.split("@", 1)
    if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]):
        # If the path contains '@' and the part before it does not look
        # like a path, try to treat it as a PEP 440 URL req instead.
        return None
    logger.warning(
        "Requirement %r looks like a filename, but the file does not exist",
        name,
    )
    return path_to_url(path)

View on GitHub (pinned to f399c37189)