The pip error externally-managed-environment appeared on millions of Linux and macOS machines during 2023 and confused everyone who hit it for the first time. The short version: your operating system marked its bundled Python as off-limits to pip because the OS itself owns those packages. The marker is a real file (you can read it), the standard is PEP 668, the rollout dates are public, and the five fix paths each have a clear best-use case. This guide covers what changed, why it's a good change even though it broke your muscle memory, and how to pick the right fix instead of reaching for --break-system-packages as a reflex. It's one of 10 explainers in our Python Environment Setup pillar page.

Key Takeaways

  • It's not a bug; it's a guardrail. PEP 668 protects OS-owned Python from pip overwriting files apt/dnf/brew depend on.
  • The marker is a real file you can read. /usr/lib/python3.12/EXTERNALLY-MANAGED on Linux; the contents tell pip what error to print.
  • Five fix paths exist. venv (default), uv (modern default), pipx (CLI tools), apt/dnf/brew (system deps), --break-system-packages (last resort).
  • Don't reflex to --break-system-packages. It can break your OS package manager months later. Use it in containers and CI, never on a real workstation.
  • uv sidesteps the whole thing. It installs and manages its own Python, so the OS marker never applies.
OS-owned Python boundary vs user-space install paths The OS-owned boundary Where pip can install and where it can't, post-PEP 668 OS-owned (locked) /usr/bin/python3 managed by apt / dnf / brew /usr/lib/python3.12/ EXTERNALLY-MANAGED ← Contains: [externally-managed] Error=To install Python packages system-wide, try apt install python3-<pkg> or create a venv. User space (open) ~/proj/.venv/ python -m venv .venv ~/.local/share/uv/python/ uv python install 3.13 ~/.local/pipx/venvs/ pipx install black All three: pip can install freely pip install <pkg> blocked allowed where does it land? (decided by whether an env is active)
The OS owns its Python install. Everything you install with pip belongs in user space: a venv, a uv-managed Python, or a pipx-managed tool environment.

The Error in Full

The exact text varies slightly by distro. Here's what you'll see on Debian 12 or Ubuntu 23.04+:
$ pip install requests
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip.

    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.

    See /usr/share/doc/python3.12/README.venv for more information.

note: If you believe this is a mistake, please contact your Python
      installation or OS distribution provider. You can override this,
      at the risk of breaking your Python installation or OS, by
      passing --break-system-packages.
The error is doing two things: telling you the install is refused, and reading you the distro's suggested fix paths verbatim. Those paths come from a file you can inspect.

The EXTERNALLY-MANAGED File

Pip detects the marker by looking for EXTERNALLY-MANAGED in the standard library directory of the running Python. On a fresh Ubuntu 24.04:
$ cat /usr/lib/python3.12/EXTERNALLY-MANAGED
[externally-managed]
Error=To install Python packages system-wide, try apt install
 python3-xyz, where xyz is the package you are trying to
 install.

 If you wish to install a non-Debian-packaged Python package,
 create a virtual environment using python3 -m venv path/to/venv.
 Then use path/to/venv/bin/python and path/to/venv/bin/pip.

 If you wish to install a non-Debian packaged Python application,
 it may be easiest to use pipx install xyz, which will manage a
 virtual environment for you. Make sure you have pipx installed.

 See /usr/share/doc/python3.12/README.venv for more information.
The format is configparser INI: one section [externally-managed] with one key Error whose multi-line value pip prints back to you. Each distro customizes the message:
Distro / SourcePathPrimary suggestion in Error key
Debian, Ubuntu/usr/lib/python3.X/EXTERNALLY-MANAGEDapt install python3-<pkg>
Fedora, RHEL/usr/lib/python3.X/EXTERNALLY-MANAGEDdnf install python3-<pkg>
Arch Linux/usr/lib/python3.X/EXTERNALLY-MANAGEDpacman -S python-<pkg>
Homebrew Python (macOS)$(brew --prefix)/lib/python3.X/EXTERNALLY-MANAGEDbrew install or pipx
python.org installernot presentno marker, no error
If your distro's Python doesn't have the file, you won't see the error. That's by design: the python.org installer, manually built Python, and uv-managed Python all skip the marker because they aren't owned by an OS package manager.

Why This Is a Good Change

For a decade before PEP 668, pip would silently overwrite OS-managed Python packages. Most of the time nothing bad happened. Occasionally apt or dnf would break: you ran pip install --upgrade urllib3, and a few weeks later apt update failed because its Python helpers depended on the older urllib3 you replaced. Those bugs were hard to trace because the immediate symptom (apt broken) didn't point back at the cause (pip overwriting a shared dep months earlier).

PEP 668 makes the failure loud and immediate instead of quiet and delayed. You see the error the moment you try to install; the suggested fix paths all involve installing somewhere else; the OS package manager keeps working. The change broke "I just want to install a package" muscle memory, but the muscle memory was building a slow-motion footgun. The new error message hands you that footgun's safety catch already engaged."externally-managed-environment" Pip Error: What PEP 668 Changed and the Five Fix Paths - 1

The Five Fix Paths

Fix Path 1: Virtual Environment (Default Answer)

This is the right answer for almost every project-specific package install.
# Make a venv for this project
python3 -m venv .venv

# Activate it
source .venv/bin/activate     # macOS/Linux
.venv\Scripts\activate        # Windows

# Now pip works because you're inside an isolated env
pip install requests
The venv isn't OS-owned, so PEP 668 doesn't apply. Every package you install goes into the venv's own site-packages, leaving system Python untouched. For the deeper venv mechanics, see our venv vs uv vs Poetry decision guide.

Fix Path 2: uv (Modern Default)

uv ships its own Python, lives in your home directory, and never touches the system Python. The EXTERNALLY-MANAGED file simply doesn't apply to anything uv does.
# Install uv (one-line bootstrap)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install a Python managed by uv, isolated from the system
uv python install 3.13

# Create a project that uses it
uv init myproject
cd myproject
uv add requests        # works; no EXTERNALLY-MANAGED check
This is the cleanest path if you're starting fresh and want to skip the venv lifecycle entirely. uv manages everything per-project including the Python version. See docs.astral.sh/uv for the full feature set.

Fix Path 3: pipx (for CLI Tools)

When the thing you want to install is a Python application you'll call from the shell (not a library you'll import), pipx is the right path.
# Install pipx itself, often available from the OS package manager
sudo apt install pipx       # Debian/Ubuntu
brew install pipx           # macOS Homebrew

# Then install CLI tools into per-tool isolated envs
pipx install black
pipx install httpie
pipx install ruff
pipx ensurepath              # adds ~/.local/bin to PATH if needed
pipx creates an isolated venv per tool and symlinks the entry-point binary onto your PATH. The system Python stays untouched. For more on pipx vs uv as global-tool installers, see pipx.pypa.io comparisons.

Fix Path 4: System Package Manager

If the package you want is also packaged by your distro, install the distro's version. This is the right path when the package needs to integrate with other OS components.
# Debian/Ubuntu
sudo apt install python3-requests

# Fedora/RHEL
sudo dnf install python3-requests

# Arch
sudo pacman -S python-requests

# macOS Homebrew
brew install python-requests       # if available; usually you'd use a venv instead
This is the path the EXTERNALLY-MANAGED error message itself suggests first. The trade-off: distro packages often lag the latest PyPI release, you can only install what the distro has packaged, and you can't pin a specific version for your project. For most application-level deps, a venv beats this.

Fix Path 5: --break-system-packages (Last Resort)

The footgun. This flag does what the name says. It bypasses PEP 668 and installs into system Python, which can overwrite files apt/dnf/brew depend on and break OS tools weeks or months later.
# Per-command (when you've thought about it):
pip install --break-system-packages requests

# Globally via pip config (DON'T do this on a workstation):
pip config set global.break-system-packages true

# Globally via env var:
export PIP_BREAK_SYSTEM_PACKAGES=1
Legitimate uses. Docker base images where the container is ephemeral, one-shot CI runners where nothing else depends on the system Python, and tightly-scoped automation scripts where you've audited every package you install. In every other case, take 30 seconds to make a venv instead.

Decision Table: Which Fix Path Do I Want?

Your situationUseWhy
Project that needs specific depsvenvIsolated, scoped to this project
Starting fresh in 2026uvFaster, manages Python too, no marker applies
Installing CLI tools (black, ruff, httpie)pipx or uv tool installPer-tool isolation, binary on PATH
Need the package OS-wide and distro has itapt install python3-pkgOS-managed, integrates with the system
Docker base image build script--break-system-packagesEphemeral container, safe blast radius
CI runner, single-run--break-system-packages or a venvEither works, venv is cleaner
Your laptop, "I just want to install something"venv30 seconds, no surprises later

What About Docker?

Many official Python Docker images don't ship the EXTERNALLY-MANAGED marker, which is intentional: the container is the isolation boundary, so PEP 668 protection would be redundant. The python:3.13-slim image installs directly into system Python and that's fine. Distro-based images (ubuntu:24.04, debian:12) DO include the marker because they're packaging the distro's Python.

In a Debian-based Docker build, either explicitly opt out (ENV PIP_BREAK_SYSTEM_PACKAGES=1) or make a venv in the image (RUN python3 -m venv /opt/venv && PATH=/opt/venv/bin:$PATH). The venv path is cleaner; it makes the image smaller because pip won't try to track the system packages.

Edge Case: pip Config and EXTERNALLY-MANAGED Together

You can tell pip to skip the check via config:
# See current pip config
pip config list

# Set break-system-packages globally for the current user
pip config set global.break-system-packages true
This is the same risk profile as the flag, applied to every future pip command from the current user. Setting it on a development laptop is almost always wrong. If you find yourself wanting to set it because the error is annoying, the right answer is making a venv your shell auto-activates per directory (with direnv or your shell's per-folder hook) so the venv is just always there when you cd into a project.

What Removed in 2026: Nothing Yet

PEP 668 is stable; the spec moved from PEP form to the canonical packaging.python.org spec in 2024 but the behavior didn't change. Distros continue to adopt it on newer Pythons. If you upgrade from Ubuntu 22.04 (no marker) to Ubuntu 24.04 (marker present) you'll see the error for the first time and remember reading this page. The mental model stays the same: the OS owns its Python, you install elsewhere.

Frequently Asked Questions

What does "error: externally-managed-environment" mean?

It means your operating system marked the system Python as off-limits to pip, because the OS package manager (apt, dnf, brew) owns those packages. PEP 668 standardized this in 2022, and most distros adopted it during 2023: Debian 12, Ubuntu 23.04, Fedora 38, Arch, and Homebrew Python. The marker is a real file named EXTERNALLY-MANAGED in your Python's stdlib directory. When pip sees that file, it refuses system-wide installs and prints the error. The fix is to install elsewhere: a virtual environment, a pipx-managed tool, an apt/dnf/brew package, or uv's managed Python.

Is --break-system-packages safe?

Sometimes, in containers and CI. Almost never on a real workstation. The flag bypasses PEP 668 and installs into system Python anyway, which can overwrite files the OS package manager owns and break OS tools that depend on a specific Python package version. On a Docker base image or a one-shot CI runner the blast radius is contained, so the flag is acceptable as a deliberate choice. On your laptop or a long-lived server, the safer move every time is a venv or pipx; the small inconvenience is worth not surprise-breaking apt or dnf months later.

What is the EXTERNALLY-MANAGED file?

A plain-text marker file the distro places at the root of its Python install (for example /usr/lib/python3.12/EXTERNALLY-MANAGED). It contains an [externally-managed] INI section with an Error key whose value is the message pip prints back to you. Distros customize the message to point at their preferred fix path: Debian and Ubuntu mention apt install python3-xyz, Homebrew mentions brew install. You can cat it yourself to see what your distro is suggesting. Deleting the file is technically a fix, but it's the same thing as --break-system-packages with extra steps.

Why did this error suddenly start appearing in 2023?

PEP 668 was accepted in June 2022, and distros rolled it out across 2023. Debian 12 (Bookworm), Ubuntu 23.04 (Lunar Lobster), Fedora 38, Arch, and Homebrew all added the EXTERNALLY-MANAGED file to their packaged Python. Before that, pip would happily install over the OS-managed Python and occasionally break apt, dnf, or brew when shared dependencies (urllib3, requests, PyYAML) got upgraded out from under them. The error is the loud version of a quiet decade-long problem; the fix is to stop installing into system Python and use a venv, uv, or pipx instead.

Should I install Python from python.org to avoid this error?

It works (the python.org installer doesn't ship the EXTERNALLY-MANAGED file), but it's solving the wrong problem. The PEP 668 marker exists to protect your OS from pip overwriting it; installing a parallel Python from python.org just means you have two Pythons to keep updated. The cleaner fix is to use a venv (or uv-managed Python) for every project. If you genuinely don't want to deal with the OS Python at all, uv installs and manages its own Pythons and gives you both faster installs and the cleanest separation from the OS.

The Bottom Line: Install Where pip Is Allowed, Not Where It Used to Be

The error means the OS reserved its Python for itself. Make a venv (default), pick uv (modern), use pipx (for CLI tools), or let the system package manager do it. Reach for --break-system-packages only in containers and CI. The change broke the habit of typing pip install globally; it didn't break anything important. For deeper context, see our venv decision guide, the multiple Pythons guide, or browse the full Python Environment Setup pillar page.

Skip the System Python Wars

CodeGym's Python track runs in a browser-based WebIDE. No system Python, no PEP 668 marker, no pip permissions to worry about. 800+ hands-on tasks across 62 levels, an AI validator that checks every submission instantly, and an AI mentor when an error stumps you. First level free; full plan on the pricing page. Begin your Python learning path →