pypa/pip · error · InstallationError

Directory {name!r} is not installable. Neither 'setup.py' no

Error message

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

What it means

Raised by _get_url_from_path() when the path passed to install_req_from_line looks like a path and resolves to a directory, but is_installable_dir() returns False — meaning the directory lacks both setup.py and pyproject.toml. pip will not treat a metadata-less directory as an installable project (constructors.py:315).

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 d7d0d0a394)

Solutions

  1. Confirm the target directory contains pyproject.toml or setup.py: `ls <dir>`.
  2. Point pip at the directory that actually holds the project metadata (often one level up or down).
  3. If the project is elsewhere, pass the correct absolute path.
  4. If you intended to install a package by name from PyPI, remove the path prefix and use the distribution name instead.

Example fix

# before
pip install ./docs
# after
pip install ./pkg
Defensive patterns

Strategy: validation

Validate before calling

import os
from pip._internal.utils.misc import is_installable_dir

def assert_dir_installable(path: str) -> None:
    if os.path.isdir(path) and not is_installable_dir(path):
        raise ValueError(
            f"{path!r} is a directory but lacks setup.py and pyproject.toml"
        )

Type guard

import os

def dir_has_project_metadata(path: str) -> bool:
    return os.path.exists(os.path.join(path, "pyproject.toml")) or \
           os.path.exists(os.path.join(path, "setup.py"))

Try / catch

null

Prevention

When it happens

Trigger: Running `pip install ./some_dir` where some_dir exists and is a directory but contains neither setup.py nor pyproject.toml. Distinct from error 120 (which fires later in load_pyproject_toml); this one fires earlier during path-based requirement parsing.

Common situations: Pointing at a documentation/ or tests/ directory by mistake. A monorepo where the installable package is nested deeper. An empty scaffold directory created by a generator that was never populated.

Related errors


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