pypa/pip · error · InstallationError

is not a valid editable requirement. It should either be a…

Error message

{editable_req} is not a valid editable requirement. It should either be a path to a local project or a VCS URL (beginning with {backends}).

What it means

Raised by parse_editable (constructors.py:159) when an editable requirement string parses to a URL that is neither a VCS URL (git+/hg+/svn+/bzr+) nor a file:// path. Editable installs require either a local project path or a VCS checkout URL; anything else is rejected with the list of accepted VCS schemes.

Solutions

  1. For a local project, give the directory path: 'pip install -e ./path/to/project'.
  2. For a remote repo, use a VCS scheme: 'pip install -e git+https://github.com/x/y.git'.
  3. If you only need a non-editable install from a URL, drop the -e flag and use a direct URL/reference.

Example fix

# before
pip install -e https://github.com/user/repo
# after
pip install -e git+https://github.com/user/repo.git
# or local
pip install -e ./myproject
Defensive patterns

Strategy: validation

Validate before calling

from pip._internal.vcs import vcs

def is_valid_editable_target(line: str) -> bool:
    low = line.lower()
    if low.startswith('file:'):
        return True
    import os
    if os.path.isdir(line):
        return True
    return any(low.startswith(s + '+') or low.startswith(s + ':') for s in vcs.all_schemes)

if __name__ == '__main__':
    import sys
    if not is_valid_editable_target(sys.argv[1]):
        print(f'{sys.argv[1]!r} is not a local path or VCS URL'); sys.exit(1)

Type guard

from pip._internal.vcs import vcs
import os

def is_editable_compatible(line: str) -> bool:
    low = line.lower()
    return (low.startswith('file:')
            or os.path.isdir(line)
            or any(low.startswith(s + '+') or low.startswith(s + ':') for s in vcs.all_schemes))

Try / catch

from subprocess import run, CalledProcessError
try:
    run(["pip", "install", "-e", target], check=True)
except CalledProcessError:
    # Convert http(s) URL to a VCS URL or use non-editable install.
    run(["pip", "install", "-e", "git+" + target], check=True)

Prevention

When it happens

Trigger: Passing '-e <something>' where <something> resolves to an http(s) URL, an unsupported scheme, or a non-path string that isn't recognized as file: or a VCS scheme. Checked via link.is_vcs and link.url.startswith('file:').

Common situations: Typing 'pip install -e https://github.com/x/y' instead of 'pip install -e git+https://...'; using an http:// URL for editable install; a path that doesn't exist and isn't converted to file:.

Related errors


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

Appendix: source

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

    """Parses an editable requirement into:
        - a requirement name with environment markers
        - an URL
        - extras
    Accepted requirements:
        - svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
        - local_path[some_extra]
        - Foobar[extra] @ svn+http://blahblah@rev#subdirectory=subdir ; markers
    """
    try:
        package_name, url, extras = _parse_direct_url_editable(editable_req)
    except ValueError:
        package_name, url, extras = _parse_pip_syntax_editable(editable_req)

    link = Link(url)

    if not link.is_vcs and not link.url.startswith("file:"):
        backends = ", ".join(vcs.all_schemes)
        raise InstallationError(
            f"{editable_req} is not a valid editable requirement. "
            f"It should either be a path to a local project or a VCS URL "
            f"(beginning with {backends})."
        )

    # The project name can be inferred from local file URIs easily.
    if not package_name and not link.url.startswith("file:"):
        raise InstallationError(
            f"Could not detect requirement name for '{editable_req}', "
            "please specify one with your_package_name @ URL"
        )
    return package_name, url, extras


def check_first_requirement_in_file(filename: str) -> None:
    """Check if file is parsable as a requirements file.

    This is heavily based on ``pkg_resources.parse_requirements``, but

View on GitHub (pinned to f399c37189)