ModuleNotFoundError: No module named 'oauthlib' after pip install
Fixes ModuleNotFoundError: No module named 'oauthlib' when pip install already succeeded. Use when pip show lists oauthlib or requests-oauthlib but the import still fails in the running code, usually a pip and python interpreter mismatch, a pip versus pip3 split, or a no-deps install that dropped the transitive oauthlib. Not for OAuth flow errors after oauthlib imports.
TL;DR: if pip install oauthlib, or pip install requests-oauthlib, reported success and the import still fails, one of two things happened. Either pip installed into a different Python than the one running your code, or oauthlib never actually arrived because it is normally a transitive dependency of requests-oauthlib and something installed that package with dependencies skipped. Do not reinstall with bare pip. Check what the failing interpreter itself can see with python -m pip show oauthlib, then install through that interpreter with python -m pip install oauthlib, and declare oauthlib directly in your project dependencies.
ModuleNotFoundError: No module named 'oauthlib'Search that created the source thread:
oauthlib+No+module+named+pip+installSteps
1. Print the interpreter that actually fails
Run this in the exact context that fails: the same terminal, working directory, and launch method as the failing script. Not in a different shell you happen to have open.
python -c "import sys; print(sys.executable); print(sys.version)"
python3 -c "import sys; print(sys.executable); print(sys.version)"Expected output: one path per command, for example a path inside a project .venv folder, a system python3 path, or a conda env path. Success check: note the path produced by the command that matches how the code is really launched. An IDE run button, a Jupyter kernel, cron, a service unit, and a Docker entrypoint can each use a different interpreter.
2. Ask that interpreter what it can see, not bare pip
This is the step that splits the two causes apart.
pip --version
pip show oauthlib
python -m pip show oauthlib
python -m pip show requests-oauthlibExpected output and how to read it:
- Bare pip show finds oauthlib, but python -m pip show oauthlib says the package is not found. That is cause one: an interpreter mismatch. Bare pip and the running python are different environments.
- python -m pip show requests-oauthlib finds the wrapper, but python -m pip show oauthlib finds nothing. That is cause two: the wrapper was installed with dependencies skipped, so its oauthlib dependency never landed.
- Both python -m pip commands find their packages, with a Location inside the step 1 environment, but the import still fails. Skip to step 6; something is shadowing or the running process is stale.
Success check: you know which cause you have before you install anything else.
3. Install through the failing interpreter
python -m pip install oauthlibIf the failing code is requests based, install the wrapper the same way so both halves live in one environment. The PyPI name uses a dash; the import name uses an underscore.
python -m pip install requests-oauthlibVariants, same idea: if the code runs under python3, use python3 -m pip install oauthlib, which also settles the common pip versus pip3 split where pip points at an old or system Python and pip3 points at the current one. On Windows, when the code is started with the py launcher, use py -m pip install oauthlib. In Jupyter, run %pip install oauthlib in a cell so the install targets the kernel, then restart the kernel. In conda, activate the env first and install inside it.
Expected output: Successfully installed oauthlib-3.2.2 or a similar 3.x version, or a Requirement already satisfied line whose Location is inside the step 1 environment. oauthlib is pure Python with no dependencies of its own, so this install is small and fast; a long build or a compiler error means you are looking at a different problem. Success check: the install Location and sys.executable share the same environment prefix.
4. If the transitive dependency was dropped, fix the install that dropped it
Search your Dockerfile, CI config, and requirements install scripts for a no-deps flag on the requests-oauthlib install, or for a lockfile or vendored install that lists requests-oauthlib without oauthlib. Reinstalling the wrapper normally, as in step 3, restores oauthlib once, but the next clean build drops it again until that flag or lockfile is fixed. The durable fix is twofold: remove the dependency-skipping flag for this package, and declare oauthlib directly (step 5) so no resolver change can silently remove it again.
Expected output: a clean rebuild of the image or environment passes the step 5 import check with no manual install in between. Success check: the dependency file or lockfile now names oauthlib explicitly.
5. Verify in the same interpreter, then make it permanent
python -c "import oauthlib; print(oauthlib.__version__); print(oauthlib.__file__)"
python -c "from requests_oauthlib import OAuth2Session; print('ok')"Expected output: a 3.x version number, a file path inside the step 1 environment, and ok from the second command (run the second only if your code uses requests-oauthlib). Success check: run the original script the original way, not just an interactive import.
Then declare the dependency so a fresh environment cannot recreate this: add oauthlib to requirements.txt, or as a dependency in pyproject.toml, pinned or bounded the way your project handles other dependencies. Add it even if your own code only imports requests_oauthlib or imports names like oauthlib.oauth2 through another library; a package your code cannot run without should be a direct dependency, not a transitive hope.
6. Rule out the remaining hiding places
If steps 1 to 5 line up and the import still fails, check these in order:
- Virtualenv not activated in the shell that ran the install. Activation lasts one terminal session. Activate the env, confirm sys.executable points inside it, then repeat step 3.
- Editor interpreter. Select the interpreter whose path equals the step 1 output. The integrated terminal can be right while the run button uses another env.
- A different user ran the install. An install done with elevated privileges or a user-only flag is visible to one user and interpreter and invisible to a service account, cron job, or web worker. Repeat step 3 as the user and interpreter that run the code.
- A local file shadows the name. A file or folder named oauthlib in your project directory sits ahead of site-packages on sys.path; a broken or empty one gets picked up instead of the installed package. Rename it and retry, and print oauthlib.file to confirm which file was loaded.
- PYTHONPATH points at another environment. Print sys.path in the failing run and look for site-packages folders from a different venv or Python version ahead of the correct one. Remove the stale entry instead of installing around it.
- Stale process. Restart long-running workers, notebook kernels, and debug sessions that started before the install; a fresh process is the honest test.
Expected output: one of the six explains the case, or the import now works. Success check: the import succeeds when the code is launched the production way.
When this applies
- pip install oauthlib or pip install requests-oauthlib reported success, and import oauthlib still raises ModuleNotFoundError.
- The traceback surfaces deep inside another library, for example a line in requests_oauthlib importing from oauthlib, or code importing oauthlib.oauth2 directly.
- pip show oauthlib finds the package, but python -m pip show oauthlib in the running environment does not, or shows a different Location.
- requests-oauthlib is present in the right environment but oauthlib is not, after an install that skipped dependencies.
- The code works in one terminal and fails in an IDE, Jupyter, cron, a service, CI, or a container.
When this does not apply
- No module named requests_oauthlib. That is the wrapper package itself missing; install requests-oauthlib through the running interpreter. The two errors look alike and are often confused.
- oauthlib was never installed anywhere, in any environment. The short form still works: activate the venv, run python -m pip install oauthlib, and confirm with step 5.
- oauthlib imports and the failure is an OAuth flow problem instead: an invalid grant, a redirect mismatch, an insecure transport error raised for a plain http callback URL, or a provider rejecting the client. Those mean oauthlib is present and working; the configuration is the problem.
- The error names a different missing module inside your OAuth stack, such as requests itself. Fix that module's install the same interpreter-first way.
Variant: traceback points inside requests_oauthlib
Your code may never import oauthlib by name; the import happens inside requests_oauthlib when it loads its session classes. That is why installing only the wrapper, with dependencies skipped, produces an error naming a package you never asked for. Treat the named module, oauthlib, as the thing to install and declare.
Variant: import works in terminal, fails in Jupyter
The notebook kernel is its own interpreter. Print sys.executable in a cell, install with the %pip magic so the install targets the kernel environment, and restart the kernel.
Variant: works locally, fails in Docker or CI
The image or CI job installed with one pip and runs with another python, or a dependency-skipping install dropped oauthlib at build time. In the Dockerfile or CI config, create the venv first, do every install and the final run with that venv python via python -m pip, and check the build for the flag described in step 4.
Variant: pip and python report the same version but still mismatch
Same version number is not the same interpreter: python 3.12 from the OS and python 3.12 from Homebrew or conda are different installs with different site-packages. Trust full paths, not version numbers. Compare sys.executable, the pip --version path, and the Location lines.
Why it happens
Two independent causes produce the same error after a successful-looking install. First, pip is a separate program with its own target environment, and a machine normally has several Pythons; bare pip installs into whichever environment that pip script was built for, while the running interpreter only searches its own sys.path, so the package can genuinely exist on disk and genuinely not exist for that interpreter. Second, oauthlib is usually not installed by name at all: it arrives as a dependency of requests-oauthlib or another OAuth library, so anything that trims transitive dependencies, skips them, or freezes a broken lockfile removes a package nobody listed. Invoking pip as a module of the target interpreter removes the first ambiguity, and declaring oauthlib directly removes the second.
Compatibility
oauthlib 3.x (3.2.x current) on supported CPython 3 versions, Linux, macOS, and Windows. oauthlib is pure Python with no dependencies of its own. requests-oauthlib 1.3 and 2.x require oauthlib 3.x and requests 2.x, both resolved automatically by a normal install. The python -m pip pattern works with venv, virtualenv, and conda environments and with every pip in current use.
Edge cases and pitfalls
- Do not fix this with repeated bare pip installs; each one succeeds into the same wrong environment and the error does not change.
- Requirement already satisfied can be the bug, not a reassurance: read its Location. If that folder is not on the failing interpreter sys.path, it was satisfied in the wrong env.
- Watch the dash and underscore: the install name is requests-oauthlib, the import name is requests_oauthlib, and the module in this error, oauthlib, is spelled the same as its install name.
- Avoid elevated-privilege installs to make it work; they install for the system interpreter as another user and usually make the service and venv cases worse.
- Do not copy the oauthlib folder between environments. Declare it and let pip install it per environment.