pip install 이 externally-managed-environment 오류를 낼 때: 맥 Homebrew 파이썬에서 막히는 명령과 안 막히는 명령, 가상환경으로 푸는 방법을 직접 시험한 결과When pip install fails with externally-managed-environment: which commands Homebrew Python on a Mac blocks and which it allows, and how to fix it with a virtual environment, tested directly
맥에서 pip install 이 externally-managed-environment 오류로 끝난 독자가, 어느 명령이 왜 막히는지 알고 시스템 파이썬을 건드리지 않는 설치 방법을 고르게 돕는다.
이 맥(macOS 26.6.2)의 Homebrew 파이썬 3.14.7 은 EXTERNALLY-MANAGED 표식 파일이 있어 pip install·uninstall 이 종료 코드 1로 막히고, 가상환경 안의 pip 는 같은 표식 파일이 보여도 막히지 않았다. 가상환경을 만들어 그 안의 pip 로 설치하면 바깥 python3 는 그대로다.
프로젝트 폴더에서 python3 -m venv .venv 를 만들고 .venv/bin/pip install 로 설치한 뒤, 스크립트와 CI 에서도 .venv/bin/python 을 직접 부르도록 바꿔 보라.
목차
한 줄 요약: 맥의 Homebrew 파이썬은 시스템 전체에 pip 로 설치하는 것을 표식 파일로 막아 두었으니, 프로젝트마다 가상환경을 만들어 그 안의 pip 로 설치하면 된다.
맥에서 pip3 install six 를 쳤더니 error: externally-managed-environment 가 나오고 설치가 막혔다. 이 글의 시험에서 --dry-run 을 붙인 같은 명령은 이 오류와 종료 코드 1 로 끝났다. 이 글은 그 오류가 어디서 나오는지, 어떤 pip 명령이 막히고 어떤 명령은 통과하는지, 가상환경과 uv 로 어떻게 푸는지, 오류를 흘려보내면 스크립트가 어디서 터지는지를 이 맥에서 직접 쟀다. 실제로 깐 것은 임시 폴더의 가상환경과 --target 폴더, uv 의 임시 환경뿐이고, 설치 줄은 --dry-run(설치하는 시늉만 하고 아무것도 깔지 않는 pip 옵션)으로 확인했다.
오류의 정체는 파이썬 폴더 안의 파일 하나다
시험한 맥은 macOS 26.6.2, Homebrew 파이썬 3.14.7, pip 26.2.1 이다. 이 파이썬의 표준 라이브러리 폴더(sysconfig.get_path("stdlib") 가 알려 주는 곳)에 EXTERNALLY-MANAGED 라는 파일이 있다. 첫 줄은 [externally-managed] 이고 그 아래 Error= 로 시작하는 안내문이 오류 화면에 그대로 찍힌다. Xcode 와 함께 오는 /usr/bin/python3(3.9.6)에는 같은 폴더에 이 파일이 없었다.
표식 파일의 규칙은 파이썬 개선안 PEP 668(상태 Final)에 있다. 설치 도구는 먼저 “Is it running outside of a virtual environment? It can determine this by whether sys.prefix == sys.base_prefix” 를 보고, 표식 파일이 있으면 설치를 거부해야 한다. 같은 문서는 사용자가 이 규칙을 넘을 방법으로 “a command-line flag –break-system-packages” 를 예로 든다. 오류 화면이 권하는 순서도 이렇다: brew install, 가상환경, pipx(이 맥에는 없어 시험하지 못했다), 마지막이 --break-system-packages 이다.
막히는 명령과 안 막히는 명령
시스템 파이썬을 건드리지 않으려고 설치 줄은 --dry-run 으로 쟀다. uninstall 은 six 가 깔려 있지 않은 채로, download 는 임시 폴더로 받게, freeze 는 그대로 실행했다. 막히면 error: externally-managed-environment 와 종료 코드 1, 통과하면 종료 코드 0 이었다(설치 줄과 download 는 Collecting six 부터, freeze 는 설치된 패키지 목록이 나왔다).
| 명령 | 결과 |
|---|---|
pip3 install --dry-run six |
막힘 |
python3 -m pip install --dry-run six |
막힘 |
pip3 install --dry-run -r req.txt |
막힘 |
pip3 install --dry-run --user six |
막힘 |
pip3 uninstall -y six |
막힘 |
pip3 install --dry-run --target 폴더 six |
통과 |
pip3 install --dry-run --prefix 폴더 six |
통과 |
pip3 download --no-deps -d 폴더 six |
통과 |
pip3 freeze |
통과 |
pip3 install --dry-run --break-system-packages six |
통과 |
--user 를 붙여도 막히는 점이 눈에 띈다. 표식 파일의 안내문은 --break-system-packages 를 쓸 때 --user 도 같이 주라고 하는데, --user 만으로는 열리지 않았다. 환경 변수 PIP_BREAK_SYSTEM_PACKAGES=1 과 설정 파일의 break-system-packages = true(임시 파일을 PIP_CONFIG_FILE 로 지정)도 각각 같은 --dry-run 을 통과시켰다. 이 우회로 실제 설치를 하면 Homebrew 쪽 패키지와 무엇이 어떻게 엉키는지는 시험하지 않았다. 표식 파일의 안내문이 그 위험을 경고한다는 것까지만 확인했다.
--target 은 통과하지만 해결은 아니다. 임시 폴더에 실제로 깔자 six.py 가 생겼지만 PYTHONPATH 없이 python3 -c 'import six' 를 하면 종료 코드 1, ModuleNotFoundError: No module named 'six' 였다. 표식이 없는 /usr/bin/python3 -m pip install --target 폴더 six(pip 21.2.4)는 오류 없이 깔렸는데, 이 오래된 pip 로는 --target 설치만 시험했다.
가상환경은 한 번 만들면 끝난다
python3 -m venv .venv
.venv/bin/pip install six
.venv/bin/python -c 'import six; print(six.__version__)'
임시 폴더에서 이 세 줄은 모두 종료 코드 0이었고 마지막 줄에서 1.17.0 이 찍혔다(시험에서는 six 라는 글자와 파일 경로도 같이 찍었다). 가상환경 안의 파이썬에서 sys.prefix == sys.base_prefix 는 False 였고, 같은 파이썬이 표준 라이브러리 폴더의 EXTERNALLY-MANAGED 를 여전히 볼 수 있는데도(존재 확인 True) 안의 pip 는 pip install --dry-run idna 를 Would install idna-3.20 으로 통과시켰다. 가상환경을 지운 뒤 바깥 python3 -c 'import six' 는 종료 코드 1 로, 시스템 파이썬에는 six 가 남지 않았다.
가상환경의 bin/python3 은 python3.14 로 가는 링크이고, pyvenv.cfg 의 home 은 /opt/homebrew/opt/python@3.14/bin 을 가리켰다. Homebrew 가 파이썬을 다른 마이너 버전으로 올릴 때 가상환경이 어떻게 되는지는 시험하지 않았다.
터미널에서 이름이 헷갈릴 수 있다. 시험에 쓴 PATH(Homebrew 경로를 앞에 둔 값)에는 pip3 와 python3 만 있고 pip·python 은 없어서 bash -c 'pip install six' 는 command not found(종료 코드 127)였다. source .venv/bin/activate 를 하면 python·pip·python3·pip3 가 모두 가상환경 안 파일을 가리킨다. 활성화가 번거로우면 스크립트에서는 .venv/bin/pip 와 .venv/bin/python 을 경로째 불러도 되고, 아래 세 줄짜리 스크립트가 종료 코드 0 으로 six ok 를 찍었다.
set -e
python3 -m venv "$dir/.venv"
"$dir/.venv/bin/pip" install -q six
"$dir/.venv/bin/python" -c 'import six; print("six ok")'
오류를 흘려보내면 터지는 곳은 엉뚱한 줄이다
설치 줄에서 오류를 버리거나 무시한 스크립트를 두 가지 만들어 돌렸다.
set -e
pip3 install -q six 2>/dev/null || true
python3 -c 'import six; print("six ok")'
set -e 와 || true 없이 2>/dev/null 만 둔 변형도 같았다. 둘 다 설치 줄은 조용히 지나가고, 두 번째 줄에서 ModuleNotFoundError: No module named 'six' 와 종료 코드 1 로 멈췄다. import 줄의 에러 메시지에는 pip 도 표식 파일도 나오지 않는다. 같은 증상이면 pip3 install 줄을 따로 실행해 externally-managed-environment 가 나오는지부터 본다.
uv 를 쓰는 경우
이 맥에는 uv 0.11.26 이 있다. uv pip install --system --dry-run six 는 같은 표식 때문에 종료 코드 2 로 멈췄고, error: 줄이 The interpreter at …/Python.framework/Versions/3.14 is externally managed 였다. --break-system-packages 를 주면 통과했다. 우회 없이 쓰는 길은 두 가지가 통했다.
uv venv 폴더로 가상환경을 만들고uv pip install --python 폴더/bin/python six로 설치 → 가상환경 파이썬에서six 1.17.0.- 폴더에 프로젝트 파일이 없을 때
uv run --no-project --with six python -c 'import six; …'→six 1.17.0. 실행 뒤에도 바깥python3는six를 못 불러왔다.
시험하지 못한 것
Linux 배포판의 파이썬, Homebrew 가 아닌 파이썬(python.org 설치본, pyenv), pipx, brew install 로 파이썬 패키지를 받는 방법, --break-system-packages 로 실제 설치했을 때의 부작용, 파이썬 버전이 바뀐 뒤의 가상환경, Windows 는 시험하지 않았다. 시험에 쓴 패키지는 six 하나와 대조용 idna 하나뿐이다.
세 줄 요약
- Homebrew 파이썬 3.14.7 의
EXTERNALLY-MANAGED표식 때문에 pip install 은--user를 줘도, uninstall 은 그냥도 종료 코드 1 로 막히고,--target·download·freeze는 통과한다. - 프로젝트 폴더에
python3 -m venv .venv를 만들고.venv/bin/pip로 설치하면 가상환경 안에서는 막히지 않고 바깥 파이썬은 그대로이며, uv 도 같은 표식을 따른다. - 설치 오류를
2>/dev/null이나|| true로 버린 스크립트는 설치 줄이 아닌 다른 줄에서ModuleNotFoundError로 터지므로 설치 줄의 종료 코드부터 본다.
To help a reader whose pip install on a Mac ended with externally-managed-environment understand which command is blocked and why, and choose an install method that does not touch the system Python.
On this Mac (macOS 26.6.2), Homebrew Python 3.14.7 has an EXTERNALLY-MANAGED marker file, so pip install and uninstall are blocked with exit code 1, while pip inside a virtual environment was not blocked even though the same marker file is visible. Create a virtual environment and install with the pip inside it, and the outer python3 stays untouched.
In your project folder, create .venv with python3 -m venv .venv, install with .venv/bin/pip install, then change your scripts and CI to call .venv/bin/python directly.
Contents
One-line summary: Homebrew Python on a Mac blocks system-wide pip installs with a marker file, so create a virtual environment per project and install with the pip inside it.
On a Mac, pip3 install six printed error: externally-managed-environment and the install was blocked. In this article’s tests, the same command with --dry-run ended with that error and exit code 1. This article measured on this Mac where that error comes from, which pip commands are blocked and which pass, how a virtual environment and uv solve it, and where a script blows up if it swallows the error. The only things actually installed were a virtual environment in a temporary folder, a --target folder, and uv’s temporary environments; install lines were checked with --dry-run (a pip option that pretends to install and installs nothing).
The cause is one file inside the Python folder
The Mac was macOS 26.6.2 with Homebrew Python 3.14.7 and pip 26.2.1. In this Python’s standard library folder (the place sysconfig.get_path("stdlib") reports) there is a file named EXTERNALLY-MANAGED. Its first line is [externally-managed], and the text after Error= is printed as-is on the error screen. The /usr/bin/python3 (3.9.6) that ships with Xcode did not have this file in the same folder.
The rules for the marker file are in the Python enhancement proposal PEP 668 (status Final). An installer first checks “Is it running outside of a virtual environment? It can determine this by whether sys.prefix == sys.base_prefix”, and if the marker file exists it must refuse to install. The same document gives “a command-line flag –break-system-packages” as an example of how users can override the rule. The order the error screen recommends is the same: brew install, a virtual environment, pipx (not on this Mac, so not tested), and --break-system-packages last.
Commands that are blocked and commands that are not
To avoid touching the system Python, I measured install lines with --dry-run. I ran uninstall with six not installed, download into a temporary folder, and freeze as is. A blocked command printed error: externally-managed-environment with exit code 1; a command that passed had exit code 0 (install lines and download went on from Collecting six, and freeze printed the installed package list).
| Command | Result |
|---|---|
pip3 install --dry-run six |
blocked |
python3 -m pip install --dry-run six |
blocked |
pip3 install --dry-run -r req.txt |
blocked |
pip3 install --dry-run --user six |
blocked |
pip3 uninstall -y six |
blocked |
pip3 install --dry-run --target folder six |
passes |
pip3 install --dry-run --prefix folder six |
passes |
pip3 download --no-deps -d folder six |
passes |
pip3 freeze |
passes |
pip3 install --dry-run --break-system-packages six |
passes |
It is notable that adding --user is still blocked. The marker file’s message says to pass --user as well when using --break-system-packages, but --user alone did not open it. The environment variable PIP_BREAK_SYSTEM_PACKAGES=1 and break-system-packages = true in a settings file (a temporary file given through PIP_CONFIG_FILE) each let the same --dry-run through as well. I did not test what a real install through these workarounds would tangle with on the Homebrew side. I only confirmed that the marker file’s message warns about that risk.
--target passes but it is not a fix. Installing into a temporary folder for real created six.py, but without PYTHONPATH, python3 -c 'import six' gave exit code 1 and ModuleNotFoundError: No module named 'six'. /usr/bin/python3 -m pip install --target folder six, which has no marker (pip 21.2.4), installed without an error; I only tested a --target install with that old pip.
A virtual environment is made once and that is it
python3 -m venv .venv
.venv/bin/pip install six
.venv/bin/python -c 'import six; print(six.__version__)'
In a temporary folder all three lines had exit code 0, and the last line printed 1.17.0 (in the test I also printed the word six and the file path). In the Python inside the virtual environment, sys.prefix == sys.base_prefix was False, and although that Python can still see EXTERNALLY-MANAGED in its standard library folder (existence check True), the pip inside passed pip install --dry-run idna with Would install idna-3.20. After I deleted the virtual environment, the outer python3 -c 'import six' gave exit code 1, so six was not left in the system Python.
The virtual environment’s bin/python3 was a link to python3.14, and home in pyvenv.cfg pointed to /opt/homebrew/opt/python@3.14/bin. I did not test what happens to a virtual environment when Homebrew upgrades Python to a different minor version.
Names can be confusing in the terminal. In the test PATH with Homebrew’s directory first, only pip3 and python3 existed and pip and python did not, so bash -c 'pip install six' gave command not found (exit code 127). After source .venv/bin/activate, python, pip, python3, and pip3 all point to files inside the virtual environment. If activating is a nuisance, a script can call .venv/bin/pip and .venv/bin/python by full path, and the three-line script below printed six ok with exit code 0.
set -e
python3 -m venv "$dir/.venv"
"$dir/.venv/bin/pip" install -q six
"$dir/.venv/bin/python" -c 'import six; print("six ok")'
If you swallow the error, the failure shows up on the wrong line
I made two scripts that discard or ignore errors on the install line and ran them.
set -e
pip3 install -q six 2>/dev/null || true
python3 -c 'import six; print("six ok")'
The variant with only 2>/dev/null, without set -e or || true, behaved the same. In both, the install line passed silently, and the second line stopped with ModuleNotFoundError: No module named 'six' and exit code 1. The error message on the import line mentions neither pip nor the marker file. If you see this symptom, run the pip3 install line by itself first and check whether externally-managed-environment appears.
When you use uv
This Mac has uv 0.11.26. uv pip install --system --dry-run six stopped with exit code 2 because of the same marker, and the error: line was The interpreter at …/Python.framework/Versions/3.14 is externally managed. With --break-system-packages it passed with exit code 0. Two routes worked without any workaround.
- Create a virtual environment with
uv venv folderand install withuv pip install --python folder/bin/python six→six 1.17.0in the virtual environment’s Python. - With no project files in the folder,
uv run --no-project --with six python -c 'import six; …'→six 1.17.0. After it ran, the outerpython3still could not importsix.
What I did not test
I did not test Linux distribution Pythons, non-Homebrew Pythons (python.org installers, pyenv), pipx, getting Python packages with brew install, the side effects of a real install with --break-system-packages, virtual environments after a Python version change, or Windows. The tests used one package (six) and one comparison package (idna).
Three-line summary
- Because of the
EXTERNALLY-MANAGEDmarker in Homebrew Python 3.14.7, pip install is blocked with exit code 1 even with--user, and uninstall is blocked too, while--target,download, andfreezepass. - Create
python3 -m venv .venvin the project folder and install with.venv/bin/pip; it is not blocked inside the virtual environment, the outer Python stays as it was, and uv follows the same marker. - A script that discards the install error with
2>/dev/nullor|| trueblows up on a different line withModuleNotFoundError, so look at the install line’s exit code first.