FAQ: Which Binary Did python Hit? A developer published a practical FAQ arguing that coding agents and chat summaries routinely misreport which Python binary actually executed, because command names like `python` and `python3` are shell aliases rather than the paths that run. The writeup walks through reproducible checks — `type`, `command -v`, `readlink -f`, `sys.executable`, and `python3 -m pip --version` — to prove that venv activation, shebang lines, and bare `pip` calls frequently resolve to a different interpreter than the one the agent claims to have used. The piece was prepared as part of MonkeyCode's product outreach and was drafted using the company's free model access. Ever trust a chat that said tests passed? Then watch the import fail on the box? The chat summary still looks clean and final. The binary on disk is a different story. This FAQ is not about clever prompts. It is about which file actually ran. A free model will type python with confidence. A free server will resolve that name somehow. Those two steps are not one contract. Why do we keep treating them as one? Coding agents talk in command strings. Unix shells talk in inodes and argv. Do you store the inode? Or the story? I want a check you can finish in minutes. No dashboards. No vibes. No screenshots. Print paths. Print exit codes. Then decide. I draft those checks with MonkeyCode free model access. I run the same checks on the free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The product does not freeze your PATH . The box does not narrate sys.executable . You still have to print both. Every time. Hold three facts. Repeat them out loud. python is not a binary. If those feel obvious, good. Keep reading. The myths below still sneak into agent logs. python3 Developers repeat this on every new image. "The box is modern. python means three." Does it? Did you type type python yet? On some images, python is missing entirely. On others it is a leftover symlink you skipped. On others it is not the python3 you just used. Evidence is a command. Not a feeling. type python type python3 command -v python python3 readlink -f "$ command -v python3 " python3 -c "import sys; print sys.version.split 0 ; print sys.executable " Corrected model: names are aliases. Binaries are paths. Only paths execute. If command -v python prints nothing, stop. The agent's python line already failed. The chat may still say the step is done. Why would you believe that sentence? You saw python3 -m venv .venv . You saw a line that said activated. Did the next tool call share that shell? Many agent runners spawn a fresh shell. Activation is a shell function plus PATH . It dies when that shell process exits. So the venv exists as a directory. Your next python3 may still be system. That is not a model bug. That is Unix. python3 -m venv .venv new shell, no source on purpose command -v python3 printf 'VIRTUAL ENV=%s\n' "${VIRTUAL ENV-}" .venv/bin/python -c "import sys; print sys.executable " Corrected model: a venv is a folder. Activation is optional sugar for humans. Absolute .venv/bin/python is the contract. Would you pin a CI job to bare python ? I would not. Neither should the agent. The file starts with /usr/bin/env python . Looks neat. Looks intentional. Looks false. Who invoked the file, and with which argv? python3 app.py ignores the shebang. bash app.py ignores the shebang too. ./app.py uses it, if the mode allows exec. python head -n 1 app.py ls -l app.py command -v env /usr/bin/env python -c "import sys; print sys.executable " || true ./app.py || echo "direct exec failed" If the file is not executable, pause. ./app.py never ran on that box. Maybe the agent used python3 app.py . Then the shebang never mattered at all. Corrected model: shebang is a kernel feature. It applies to execve of that file path. It is not a hint the interpreter rereads. pip install hits the interpreter you used This one burns hours and lockfiles. python3 was one prefix. pip was another. Install succeeded. Import failed. Chat shrugged. It is not a mystery. It is two binaries. Did pip --version mention the same path? command -v pip pip3 python3 python3 -m pip --version pip --version || echo "bare pip missing" python3 -c "import sys; print '\n'.join sys.path :4 " Always python3 -m pip on a throwaway box. Or .venv/bin/python -m pip . Never bare pip . Bare pip is how silent no-ops get shipped. Corrected model: pip belongs to one interpreter. Installing with the wrong pip is a miss. The right interpreter never saw the wheel. The model said all tests passed. Did pytest print a real exit code? Did you capture $? , or only the prose? Chat UIs compress stdout. They hide stderr. They paraphrase tracebacks into calm English. They do not store sys.executable for you. python python3 -c "import sys; print sys.executable " .venv/bin/python -m pytest -q echo "exit:$?" Corrected model: success is an exit code. Success also names the interpreter path. A sentence from a model is a rumor. Would you merge on a rumor in code review? Then do not merge on a chat recap either. Do not argue with the chat window. Write a file. Run the file. Keep the file. This script is a labeled example. Run it locally on the target box. bash /usr/bin/env bash interp audit.sh — labeled example, run on the target box set -u echo "=== shell ===" printf 'pid=%s user=%s pwd=%s\n' "$$" "$ id -un " "$PWD" echo "=== PATH head ===" printf '%s\n' "$PATH" | tr ':' '\n' | head -n 8 echo "=== names ===" for n in python python3 pip pip3 pytest; do if command -v "$n" /dev/null 2 &1; then printf '%-8s - %s\n' "$n" "$ command -v "$n" " else printf '%-8s - MISSING\n' "$n" fi done echo "=== python3 details ===" if command -v python3 /dev/null 2 &1; then python3 - <<'PY' import sys, os print "executable", sys.executable print "prefix ", sys.prefix print "base ", getattr sys, "base prefix", "" print "version ", sys.version.split 0 print "venv? ", sys.prefix = getattr sys, "base prefix", sys.prefix print "VIRTUAL ENV", os.environ.get "VIRTUAL ENV", "" PY python3 -m pip --version || echo "python3 -m pip failed" fi echo "=== venv dir ===" if -x .venv/bin/python ; then .venv/bin/python -c "import sys; print 'venv exec', sys.executable " .venv/bin/python -m pip --version || true else echo "no .venv/bin/python" fi echo "=== reminder ===" echo "print exit:\$? after every real test command" Save it next to the project. chmod +x interp audit.sh . Run it in the same cwd the agent used. Then fill the table. The table is the artifact. The chat is not the artifact. Never was. | You saw in chat | Likely myth | Run this | Trust this instead | |---|---|---|---| | "python is available" | Myth 1 | type python; type python3 | The printed path | | "venv activated" | Myth 2 | printf '%s\n' "${VIRTUAL ENV-}" | .venv/bin/python | | "script is executable" | Myth 3 | head -n 1 app.py; ls -l app.py | argv plus mode bits | | "package installed" | Myth 4 | python3 -m pip show NAME | that command's output | | "tests passed" | Myth 5 | echo $? and sys.executable | exit code and path | Compare the left column with the right. The left column is theater from a recap. The right column is what the box did. Want a wrong-pip failure to show up fast? Use this labeled, unexecuted example. python canary interp.py — unexecuted example import sys print "CANARY", sys.executable try: import requests swap for the package you expected print "import ok", getattr requests, " file ", "?" except ImportError as exc: print "import fail", exc raise SystemExit 2 Run it two ways. Read both lines. python3 canary interp.py; echo exit:$? .venv/bin/python canary interp.py; echo exit:$? Do those two CANARY paths match? If not, which path did the agent mean? If you cannot answer, you cannot ship. sys.executable for the test command? python3 -m pip --version right now? echo $? print after pytest? If any answer is "the chat said so," stop. You are debugging prose. Not a process. This audit does not pin a lockfile. It does not replace uv or Poetry flows. It does not freeze a base image either. Shims from pyenv resolve later than you think. Print sys.executable anyway. Do not guess. The script assumes a Unix shell and head . Windows needs where and py -0p . Skip this file there. Free servers get recycled without warning. Your audit is a snapshot of one shell. Rerun it after every fresh session. Who should skip this whole approach? .venv/bin/python everywhere. A free model will still invent a python name. A free server will still pick a default binary. The audit is a bridge. It is not a guarantee. I trust three lines. Only three. Everything else is a story about commands. Stories are cheap. Paths are not cheap. Ask the chat for a recap if you want color. Then ignore the recap. Open the audit file. If those two paths disagree, the myth already won.