pypa/pip · error · ConfigurationError

Fatal Internal error [id=2]. Please report as a bug.

Error message

Fatal Internal error [id=2]. Please report as a bug.

What it means

A defensive assertion-style ConfigurationError raised in _get_parser_to_modify() when load_only is set but no parser has been registered for that configuration kind. The code itself says 'This should not happen if everything works correctly', so hitting it indicates an internal state inconsistency rather than user config misuse. It is explicitly a bug-report prompt.

Source

Thrown at src/pip/_internal/configuration.py:381

        # virtualenv config
        yield kinds.SITE, config_files[kinds.SITE]

        if env_config_file is not None:
            yield kinds.ENV, [env_config_file]
        else:
            yield kinds.ENV, []

    def get_values_in_config(self, variant: Kind) -> dict[str, Any]:
        """Get values present in a config file"""
        return self._config[variant]

    def _get_parser_to_modify(self) -> tuple[str, RawConfigParser]:
        # Determine which parser to modify
        assert self.load_only
        parsers = self._parsers[self.load_only]
        if not parsers:
            # This should not happen if everything works correctly.
            raise ConfigurationError(
                "Fatal Internal error [id=2]. Please report as a bug."
            )

        # Use the highest priority parser.
        return parsers[-1]

    # XXX: This is patched in the tests.
    def _mark_as_modified(self, fname: str, parser: RawConfigParser) -> None:
        file_parser_tuple = (fname, parser)
        if file_parser_tuple not in self._modified_parsers:
            self._modified_parsers.append(file_parser_tuple)

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({self._dictionary!r})"

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Report it as a bug upstream with the exact pip version and reproduction, since the message says so.
  2. If calling the Configuration API programmatically, ensure load() runs for the target Kind before any mutating operation and before setting load_only.
  3. Upgrade/downgrade pip to a version without the regression.
  4. Use the public 'pip config set' CLI instead of internal APIs.

Example fix

# before
cfg.load_only = kinds.USER
cfg.set_value('global.timeout', '30')

# after
cfg.load_only = kinds.USER
cfg.load()  # populates _parsers before mutating
cfg.set_value('global.timeout', '30')
Defensive patterns

Strategy: validation

Validate before calling

def can_modify(cfg, kind) -> bool:
    return bool(cfg._parsers.get(kind))
# before calling cfg.set_value(...):
assert cfg.load_only is not None and can_modify(cfg, cfg.load_only)

Type guard

def parser_ready(cfg) -> bool:
    return cfg.load_only is not None and bool(cfg._parsers.get(cfg.load_only))

Try / catch

from pip._internal.exceptions import ConfigurationError
try:
    cfg.set_value(key, value)
except ConfigurationError as e:
    if 'Fatal Internal error' in str(e):
        # ensure load() ran for load_only; this is a bug
        raise

Prevention

When it happens

Trigger: Reached only if a code path calls a mutating config operation (set_value/save/etc.) with self.load_only set to a Kind for which self._parsers[load_only] is empty - i.e. load_only was assigned without ever successfully calling load() for that variant. In normal pip use this is unreachable; it appears in tests or programmatic misuse of the Configuration API.

Common situations: Programmatically constructing a Configuration, setting load_only without calling load(), then calling set_value/save; a third-party tool vendoring pip internals and calling _get_parser_to_modify out of order; a regression in pip's own config subcommand.

Related errors


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