python-poetry/poetry · error · InvalidSourceError
The PyPI repository cannot be configured with a custom url.
Error message
The PyPI repository cannot be configured with a custom url.
What it means
Raised by Factory.create_package_source when a source entry is named 'pypi' (case-insensitive) AND also provides a 'url' key. Poetry treats PyPI as a built-in repository with a fixed URL and refuses any override. The guard is at src/poetry/factory.py:221-225 and throws InvalidSourceError.
Source
Thrown at src/poetry/factory.py:223
@classmethod
def create_package_source(
cls, source: dict[str, str], config: Config, disable_cache: bool = False
) -> HTTPRepository:
from poetry.repositories.exceptions import InvalidSourceError
from poetry.repositories.legacy_repository import LegacyRepository
from poetry.repositories.pypi_repository import PyPiRepository
from poetry.repositories.single_page_repository import SinglePageRepository
try:
name = source["name"]
except KeyError:
raise InvalidSourceError("Missing [name] in source.")
pool_size = config.installer_max_workers
if name.lower() == "pypi":
if "url" in source:
raise InvalidSourceError(
"The PyPI repository cannot be configured with a custom url."
)
return PyPiRepository(
config=config,
disable_cache=disable_cache,
pool_size=pool_size,
)
try:
url = source["url"]
except KeyError:
raise InvalidSourceError(f"Missing [url] in source {name!r}.")
repository_class = LegacyRepository
if re.match(r".*\.(htm|html)$", url):
repository_class = SinglePageRepository
View on GitHub (pinned to 92b74dcfe3)
Solutions
- Rename the source to something other than 'pypi' (e.g. 'my-mirror') and keep its url — custom sources are allowed a url.
- If you truly want to override PyPI's URL, configure it via Poetry's repositories.pypi.url setting or environment POETRY_PYPI_URL, not a source table named 'pypi'.
- Remove the url line from the 'pypi' source entry if you only wanted to disable/enable default PyPI.
Example fix
# before [[tool.poetry.source]] name = "pypi" url = "https://pypi.org/simple" # after (private mirror) [[tool.poetry.source]] name = "my-mirror" url = "https://mirrors.example.com/pypi/simple"
Defensive patterns
Strategy: validation
Validate before calling
def validate_source(source: dict) -> None:
name = source.get('name', '')
if name.lower() == 'pypi' and 'url' in source:
raise ValueError(
"Source named 'pypi' must not set 'url'; rename the source or drop the url."
) Try / catch
from poetry.repositories.exceptions import InvalidSourceError
try:
Factory.create_package_source(source, config)
except InvalidSourceError as e:
if 'cannot be configured with a custom url' in str(e):
source = {k: v for k, v in source.items() if k != 'url'} or {
**source, 'name': source['name'] + '-mirror'
}
Factory.create_package_source(source, config) Prevention
- Never name a custom source 'pypi'; reserve that name for the default index.
- Lint pyproject.toml sources in CI with a schema check that rejects name='pypi' + url.
When it happens
Trigger: Calling Factory.create_package_source({'name': 'pypi', 'url': '...'}) or having a [[tool.poetry.source]] table in pyproject.toml with name = "pypi" plus a url = "..." line. Any case variant like 'PyPI' triggers it because the check uses name.lower() == 'pypi'.
Common situations: A developer tries to point Poetry at a private mirror by reusing the name 'pypi', or migrates from pip's index-url config and copies the name verbatim. Also occurs when redirecting PyPI through a corporate proxy.
Related errors
- Missing [url] in source {name!r}.
- Missing [name] in source.
- Extra [{extra}] is not specified.
- pyproject.toml changed significantly since poetry.lock was l
- Invalid layout
AI-assisted analysis of python-poetry/poetry@92b74dcfe3 (2026-08-04).
Data as JSON: /data/errors/c3e0a8b339f979ff.json.
Report an issue: GitHub.