pypa/pip · error · ConfigurationError

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

Error message

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

What it means

Raised by Configuration.unset_value() in a branch where the in-memory config claimed the key existed, but the underlying RawConfigParser.remove_option() returned False (option not found in the section). This indicates the in-memory cache and the parser object diverged — a state pip considers impossible under normal operation and explicitly labels a bug.

Solutions

  1. Report as a bug to pip with the full command, pip version, and config file contents (https://github.com/pypa/pip/issues).
  2. Ensure no other pip process or editor is modifying the config file simultaneously.
  3. Inspect the config file manually for corruption and rewrite it if needed.
  4. Retry the unset operation after restarting the shell/interpreter to clear any stale in-memory state.
Defensive patterns

Strategy: try-catch

Try / catch

from pip._internal.exceptions import ConfigurationError
try:
    cfg.unset_value(key)
except ConfigurationError as e:
    if 'Fatal Internal error [id=1]' in str(e):
        # report and recover by reloading config from disk
        cfg.load()
    else:
        raise

Prevention

When it happens

Trigger: A genuine internal inconsistency: the _config dict has the key but the parser does not. Practically unreachable in released pip without concurrent modification, filesystem races, or a bug in _load_file/_mark_as_modified. Could surface under exotic conditions like a custom sitecustomize patching configparser.

Common situations: Concurrent pip processes editing the same config file; filesystem snapshots/races where the file changed between read and write; extremely rare — most users will never see it.

Related errors


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

Appendix: source

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

        key = _normalize_name(key)
        self._ensure_have_load_only()

        assert self.load_only
        fname, parser = self._get_parser_to_modify()

        if (
            key not in self._config[self.load_only][fname]
            and key not in self._config[self.load_only]
        ):
            raise ConfigurationError(f"No such key - {orig_key}")

        if parser is not None:
            section, name = _disassemble_key(key)
            if not (
                parser.has_section(section) and parser.remove_option(section, name)
            ):
                # The option was not removed.
                raise ConfigurationError(
                    "Fatal Internal error [id=1]. Please report as a bug."
                )

            # The section may be empty after the option was removed.
            if not parser.items(section):
                parser.remove_section(section)
            self._mark_as_modified(fname, parser)
        try:
            del self._config[self.load_only][fname][key]
        except KeyError:
            del self._config[self.load_only][key]

    def save(self) -> None:
        """Save the current in-memory state."""
        self._ensure_have_load_only()

        for fname, parser in self._modified_parsers:
            logger.info("Writing to %s", fname)

View on GitHub (pinned to f399c37189)