Why can `pip install mypkg[fast]` succeed while the extra installs nothing?
answer
- The exit status told you nothing
- A warning is not an error
- The requirements may all be gated
- --no-deps and stale wheels
- Assert on the environment, not the log
basics
~20 sBecause a request for an extra that resolves to nothing is not an error. An unknown or misspelled extra only produces a warning, environment markers on the extra's requirements can all evaluate false, and --no-deps or a stale cached wheel drops them entirely, while the command still exits zero.
solid answer
~40 sThe install command reports success in several distinct failure modes. A **misspelled or removed extra** makes pip emit `does not provide the extra` as a warning and install the base package regardless. The extra's requirements may all carry **environment markers** — `sys_platform`, `python_version`, `implementation_name` — that are false on the target, so the extra is real but empty there. **`--no-deps`**, common in layered container builds, skips extras along with everything else, since extras *are* dependencies. A **stale wheel from the cache or an older pinned version** may predate the extra. And constraint files ignore extras. All of these exit zero, so the only reliable check is on the resulting environment: assert the module is importable, or read `Provides-Extra` from the installed metadata, rather than trusting the install log.
code
python · 14 linesfrom importlib.metadata import PackageNotFoundError, metadata, requires, version
def audit(dist_name):
try:
md = metadata(dist_name)
except PackageNotFoundError:
return f"{dist_name}: not installed"
return {
"version": version(dist_name),
"declared_extras": md.get_all("Provides-Extra") or [],
"requires": requires(dist_name) or [],
}
print(audit("pip"))go deeper
Remember the core surprise: asking for an extra that does not exist is a warning, not an error, so the command still succeeds. Quote the brackets, and check afterwards that the thing you wanted is actually importable.
Be able to list several mechanisms that leave an extra empty — a misspelled or removed extra, environment markers that evaluate false, --no-deps, and an older resolved version — and to inspect the installed metadata with importlib.metadata rather than rereading the install log.
An interviewer wants the diagnosis walked end to end: confirm the resolved version and declared extras, read the markers, verify with find_spec, and then remove the silence by failing fast at startup instead of degrading into a fallback path nobody chose.
Frame it as a verification policy. Any install whose success is asserted only by an exit code will eventually ship a degraded environment; decide where in the pipeline the built image is checked against what the code needs, and whether silent degradation is ever permitted in a batch path.
### The failure this produces Take a nightly payroll CSV import that is supposed to install its accelerated reader through an extra. The image builds, the install exits zero, and the job runs — but the code was written to fall back to a hand-rolled reader when the accelerator is missing, and that fallback mishandles quoted fields containing newlines, so rows are **silently truncated** while the run stretches to six hours. Nothing failed loudly anywhere. The install succeeded; the fallback succeeded; only the numbers are wrong. This is why "the extra installed nothing but the command passed" is a genuinely senior diagnosis question. ### Why the command still exits zero **1. The extra does not exist.** A typo (`[fastt]`), a rename between releases, or an extra that was dropped from a newer version all produce the same behaviour: pip prints `WARNING: mypkg 1.2.0 does not provide the extra 'fastt'` and installs `mypkg` alone, exit status 0. Name normalization (PEP 685) folds case and runs of `-`, `_` and `.`, so it rescues `Fast_Path` → `fast-path`, but not a genuine misspelling. In CI the warning scrolls past in a log nobody reads, and with `-q` it may not even appear. **2. Every requirement is marker-gated.** Extras are ordinary `Requires-Dist` lines with an `extra == "fast"` marker, and nothing stops them carrying *more* markers: ```toml [project.optional-dependencies] fast = ["a-fast-parser; implementation_name == 'cpython' and python_version < '3.14'"] ``` On a 3.14 interpreter that requirement evaluates away and the extra is legitimately empty. Same story for `sys_platform == "win32"` when the image is Linux, or a wheel-only accelerator with no artefact for the target platform tag. **3. Dependency resolution was switched off.** `pip install --no-deps` is common in layered builds and vendoring flows, and extras are dependencies, so nothing under them is installed. The bracket syntax is accepted and then has nothing to do. **4. The version installed is not the one you read.** A pin elsewhere in the requirement set, a constraints file, or a cached wheel can resolve `mypkg` to an older release that never declared `fast`. Constraint files are the subtle one: constraints deliberately do not apply extras, so a constraint entry cannot add them and can pin you to a version that lacks them. **5. The environment is not the one being used.** An install into one virtual environment while the job runs from another interpreter is not a packaging bug at all, but it presents identically. ### How to diagnose it Stop reading the install log and interrogate the installed environment. * Ask the distribution what it *declares*: `importlib.metadata.metadata("mypkg").get_all("Provides-Extra")` lists the extra names that release actually provides. If your name is not in that list, you have cause 1. * Ask what it *requires*: `importlib.metadata.requires("mypkg")` returns the `Requires-Dist` strings including their markers, so you can see whether the extra's entries are gated by something false on this machine — cause 2. * Check the version you got, not the version you asked for: `importlib.metadata.version("mypkg")`. * Check the accelerator itself: `importlib.util.find_spec("the_module")` is the ground truth, because it is the thing the code will actually import. * Rehearse the resolution without touching the environment: `pip install --dry-run --report -` prints the resolved set as JSON, which is assertable in CI. ### How to stop it recurring The install being wrong is recoverable; the *silence* is what did the damage. Two disciplines fix that. **Fail fast, at startup.** Before the job does any work, verify that the optional module the fast path requires is importable, and exit with a message naming the exact install command if it is not. A batch job that must not silently degrade should not have a silent fallback at all — make the fallback opt-in through configuration, so degraded mode is a decision somebody took rather than an accident of the image build. **Assert on the environment in CI.** A one-line check that `find_spec` finds the accelerator, run against the built image rather than the developer's machine, catches every cause above at build time. Verifying the *outcome* is the only check that survives all five mechanisms, because each of them keeps the exit status at zero. ### The cause that is not packaging at all Before blaming the extra, confirm that the environment you installed into is the one running the code. A job launched by an absolute interpreter path, a container entrypoint that bypasses the virtual environment, or a `pip` on `PATH` belonging to a different interpreter all produce a perfect install into a place nothing reads. `sys.executable` and `sys.prefix`, printed from inside the failing process, settle it in seconds, and `python -m pip install` rather than a bare `pip` prevents the mismatch in the first place by binding the installer to the interpreter you actually named.
- Why do extras that work on a developer's machine so often vanish inside a container image?Because image builds use the flags that suppress them. `--no-deps` in a layered install skips extras entirely, a constraints file pins the distribution to a release that predates the extra, a cached or vendored wheel is reused, and the install may target a different interpreter from the one the entrypoint runs. None of these fail, so the difference only surfaces at runtime.
- How would you make an unknown extra fail a build rather than warn?Do not rely on the installer to do it. Rehearse the install with `pip install --dry-run --report -` and assert on the resolved JSON, or check the built environment directly: `importlib.metadata.metadata(name).get_all("Provides-Extra")` must contain the extra you requested, and `importlib.util.find_spec` must find the module the extra provides. Run that check as a build step, so the zero exit status is no longer what you trust.
- An extra exists and is spelled correctly, yet nothing was installed. What do you check first?The environment markers on its requirements. `importlib.metadata.requires(name)` shows each `Requires-Dist` string in full, and an entry gated on `sys_platform`, `python_version` or `implementation_name` can evaluate false on this target, leaving the extra legitimately empty. If the markers all match, look next at whether the resolved version is the one that declares the extra.
saying these in an interview costs you the question
- Trusts a zero exit status as proof the extra installed
- Thinks pip errors on an extra the package does not declare
- Forgets that --no-deps skips extras too
- Ignores environment markers on an extra's requirements
- Assumes name normalization fixes a misspelled extra
- Leaves a silent fallback in a batch job that must not degrade