{"record":{"id":"1b6b9593776bd08c","repo":"pypa/pip","slug":"sorry-url-r-is-a-malformed-vcs-url-the-format","errorCode":null,"errorMessage":"Sorry, {url!r} is a malformed VCS url. The format is <vcs>+<protocol>://<url>, e.g. svn+http://myrepo/svn/MyApp#egg=MyApp","messagePattern":"Sorry, (.+?) is a malformed VCS url\\. The format is <vcs>\\+<protocol>://<url>, e\\.g\\. svn\\+http://myrepo/svn/MyApp#egg=MyApp","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/vcs/versioncontrol.py","lineNumber":383,"sourceCode":"        information can be provided via the --username and --password options\n        instead of through the URL.  For other subclasses like Git without\n        such an option, auth information must stay in the URL.\n\n        Returns: (netloc, (username, password)).\n        \"\"\"\n        return netloc, (None, None)\n\n    @classmethod\n    def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:\n        \"\"\"\n        Parse the repository URL to use, and return the URL, revision,\n        and auth info to use.\n\n        Returns: (url, rev, (username, password)).\n        \"\"\"\n        scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)\n        if \"+\" not in scheme:\n            raise ValueError(\n                f\"Sorry, {url!r} is a malformed VCS url. \"\n                \"The format is <vcs>+<protocol>://<url>, \"\n                \"e.g. svn+http://myrepo/svn/MyApp#egg=MyApp\"\n            )\n        # Remove the vcs prefix.\n        scheme = scheme.split(\"+\", 1)[1]\n        netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme)\n        rev = None\n        if \"@\" in path:\n            path, rev = path.rsplit(\"@\", 1)\n            if not rev:\n                raise InstallationError(\n                    f\"The URL {url!r} has an empty revision (after @) \"\n                    \"which is not supported. Include a revision after @ \"\n                    \"or remove @ from the URL.\"\n                )\n            rev = urllib.parse.unquote(rev)\n        url = urllib.parse.urlunsplit((scheme, netloc, path, query, \"\"))","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/vcs/versioncontrol.py#L365-L401","documentation":"Raised by VersionControl.get_url_rev_and_auth() when the URL scheme does not contain a '+' separator, meaning the VCS prefix (e.g. 'git+', 'svn+', 'hg+') is missing. The required format is <vcs>+<protocol>://<url>. Without the '+' pip cannot determine which VCS backend to use.","triggerScenarios":"Calling get_url_rev_and_auth(url) on a URL whose urllib.parse.urlsplit scheme has no '+' character. Reached when pip processes any VCS requirement URL. For example, 'https://github.com/user/repo.git' without the 'git+' prefix.","commonSituations":"Forgetting to add the 'git+'/'svn+'/'hg+' prefix to a VCS requirement. A requirements.txt entry like '-e https://github.com/user/repo.git' instead of '-e git+https://...'. Copying a plain git URL from a browser into pip install.","solutions":["Add the correct VCS prefix to the URL: 'git+', 'svn+', 'hg+', or 'bzr+'","Ensure the format is <vcs>+<protocol>://<url>, e.g. 'git+https://github.com/user/repo.git'","For editable installs, prefix with '-e': 'pip install -e git+https://...'","Add '#egg=<projectname>' if installing by project name from VCS"],"exampleFix":"// before\npip install https://github.com/user/repo.git\n// after\npip install git+https://github.com/user/repo.git\n# or for editable\npip install -e \"git+https://github.com/user/repo.git#egg=myproject\"","handlingStrategy":"validation","validationCode":"import urllib.parse\n\nSUPPORTED_VCS = ('git', 'svn', 'hg', 'bzr')\n\ndef is_valid_vcs_url(url: str) -> bool:\n    scheme = urllib.parse.urlsplit(url).scheme\n    if '+' not in scheme:\n        return False\n    vcs, _, proto = scheme.partition('+')\n    return vcs in SUPPORTED_VCS and bool(proto)\n\n# before installing\nif not is_valid_vcs_url(url):\n    raise ValueError(f'add a VCS prefix, e.g. git+{url}')","typeGuard":"import urllib.parse\n\ndef has_vcs_prefix(url: str) -> bool:\n    return '+' in urllib.parse.urlsplit(url).scheme","tryCatchPattern":"try:\n    url, rev, auth = vc.get_url_rev_and_auth(url)\nexcept ValueError as e:\n    if 'malformed VCS url' in str(e):\n        raise ConfigError(f'prefix the URL with git+/svn+/hg+/bzr+: {e}')\n    raise","preventionTips":["Always prefix VCS URLs: git+, svn+, hg+, bzr+","Use the format <vcs>+<protocol>://<url>","Validate requirement URLs in requirements.txt before CI runs","Add '#egg=<name>' for VCS-based editable installs"],"tags":["vcs","url","requirements","installation","configuration"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}