Fixing PyQGIS Module Import Errors
Home → PyQGIS Fundamentals & Environment Setup → Debugging PyQGIS Scripts → Fixing Module Import Errors
ModuleNotFoundError: No module named 'qgis' and ImportError: DLL load failed while importing _core are the two errors you hit the moment you try to import qgis from anything other than the QGIS application itself. Both share one root cause: your Python interpreter is running outside QGIS's managed environment, so it never learns where the PyQGIS bindings and their compiled C++ dependencies live. This page is a focused, reproducible fix for that failure — the specific case of getting qgis.core to import from an external interpreter such as VS Code, PyCharm, or a plain terminal. Resolving it means either injecting QGIS's library paths at runtime or running your script through the QGIS-bundled Python executable.
Import failures are the first wall most people hit when they move from the QGIS Python Console to real scripts, so it is worth understanding why the import breaks rather than copying a fix blindly. Inside QGIS the environment is fully wired up for you; outside it, nothing is, and the linker searches the wrong directories.
Prerequisites
Before applying any fix, confirm the following so you are debugging the right layer of the problem:
- A working QGIS install. Note its exact version (Help → About) and installation type — Standalone, OSGeo4W, macOS bundle, or a Linux package. The paths below differ for each.
- The bundled Python version. PyQGIS bindings are compiled against one specific Python ABI. QGIS 3.34 and later — including the 3.44 LTR — ship Python 3.12, while the older 3.28 LTR shipped Python 3.9. You need this number to match your external interpreter.
- A 64-bit interpreter. Modern PyQGIS is strictly 64-bit; a 32-bit Python will never load
_core. - The ability to inspect your environment. You should be able to print
sys.executable,sys.version, andos.environfrom the interpreter that is failing. Most misdiagnoses come from fixing paths in one interpreter while the IDE silently runs another. If your IDE's interpreter selection is opaque, the PyQGIS Fundamentals & Environment Setup reference explains how interpreter inheritance andsite-packagesresolution work.
The Fix: Manual Path Injection
When running scripts externally, inject QGIS paths before any qgis import. Import order matters — the compiled libraries are resolved against PATH and sys.path at import time, so setting these variables afterwards has no effect. Place this block at the very top of your script:
import sys
import os
# Update to match your exact QGIS installation
QGIS_PREFIX = r"C:\Program Files\QGIS 3.44\apps\qgis" # Windows Standalone
# QGIS_PREFIX = "/Applications/QGIS.app/Contents/MacOS" # macOS
# QGIS_PREFIX = "/usr" # Linux (Debian/Ubuntu)
# Inject QGIS Python paths (order matters)
sys.path.insert(0, os.path.join(QGIS_PREFIX, "python"))
sys.path.insert(0, os.path.join(QGIS_PREFIX, "python", "plugins"))
# Set mandatory environment variables
os.environ["QGIS_PREFIX_PATH"] = QGIS_PREFIX
os.environ["PATH"] = os.path.join(QGIS_PREFIX, "bin") + os.pathsep + os.environ.get("PATH", "")
# Initialize QGIS application context
from qgis.core import QgsApplication
QgsApplication.setPrefixPath(QGIS_PREFIX, True)
qgs = QgsApplication([], False)
qgs.initQgis()
print("PyQGIS environment loaded successfully.")
Note that PYTHONHOME is intentionally omitted. Setting it in an external environment forces the interpreter to look for its own standard library inside the QGIS prefix, which frequently breaks virtual environments and triggers silent crashes. Setting QGIS_PREFIX_PATH, PATH, and sys.path is enough to load the bindings without disturbing the interpreter's own runtime.
This bootstrap is the same one used when running Python scripts outside QGIS desktop — the difference here is that we are treating it as a fix for a broken import rather than a general launch pattern, so the emphasis is on getting the three path variables exactly right for your platform.
OS-specific path differences
The one line you almost always get wrong is QGIS_PREFIX. The correct value depends on how QGIS was installed:
| Install type | Typical QGIS_PREFIX | Notes |
|---|---|---|
| Windows Standalone | C:\Program Files\QGIS 3.44\apps\qgis | Use apps\qgis-ltr for an LTR build |
| Windows OSGeo4W | C:\OSGeo4W\apps\qgis-ltr | Prefer the OSGeo4W shell for wiring PATH |
| macOS bundle | /Applications/QGIS.app/Contents/MacOS | Point at Contents/MacOS, not the .app root |
| Linux (package) | /usr | Symlinks are usually already on the path; injection is only needed inside an isolated venv |
On Linux, distribution packages typically register the bindings system-wide, so import qgis works without any injection unless you have created an isolated virtual environment. In that case, create the environment with --system-site-packages so it can see the packaged bindings.
Where Python looks, and why it misses
An import error is almost always a path problem, and seeing the search order makes it obvious which of the three fixes applies.
Interpreter mismatch is a different failure
If the path is right and the import still fails — or fails with a binary incompatibility rather than "no module named" — the interpreter is not the one QGIS was compiled against.
The wording of the error is the diagnostic: "No module named qgis" is a path problem, while an undefined symbol or an ABI complaint is a version problem — and no amount of path fixing will solve the second.
QGIS-version compatibility notes
Import errors are frequently version-mismatch errors in disguise. Keep these rules in mind:
- Match the minor Python version. Because the bindings are compiled against a specific ABI, a 3.11 interpreter will raise
ImportErroragainst a QGIS that ships 3.12. Patch releases within the same minor version (3.12.1 vs 3.12.4) are compatible; minor versions (3.11 vs 3.12) are not. - QGIS 3.34+ / 3.44 LTR → Python 3.12. These are the current targets and what the code above assumes.
- QGIS 3.28 LTR → Python 3.9. If you are pinned to this older LTR, your external interpreter must also be 3.9.
- Architecture must match. A 64-bit QGIS needs a 64-bit interpreter; the mismatch surfaces as a failure during
QgsApplicationinitialization rather than at import. - API surface drifts between releases. Even once the import succeeds, classes added in later releases will raise
AttributeErroron an older QGIS. Pin your example code to the LTR you actually run.
Troubleshooting
If path injection still throws ImportError: cannot import name 'QgsApplication', crashes silently, or reports a missing DLL, work through these fallbacks in order.
- Execute via the QGIS Python wrapper. Bypass the external interpreter entirely by calling the bundled executable, which has every path pre-configured:
- Windows:
"C:\Program Files\QGIS 3.44\bin\python-qgis.bat" your_script.py - macOS:
/Applications/QGIS.app/Contents/MacOS/bin/python3 your_script.py - Linux:
python3 your_script.pyafter sourcing the QGIS environment shell script
- Windows:
- Run inside the QGIS Python Console. Open QGIS → Plugins → Python Console and run
exec(open('your_script.py').read()). The console auto-initializesQgsApplicationand loads allqgis.*namespaces, which is the fastest way to confirm the problem is environmental rather than a bug in your code. - Remove a conflicting PyPI package. Run
pip list | grep qgis(macOS/Linux) orpip list | findstr qgis(Windows). Official PyQGIS bindings are never distributed through PyPI — the PyPIqgispackage is a documentation stub that shadows the real libraries. If you see it, uninstall it immediately withpip uninstall qgis. - Verify the native dependency chain. PyQGIS depends on compiled C++ libraries (GDAL, PROJ, Qt).
DLL load failed while importing _coremeans Python found theqgispackage but could not load those dependencies — usually because the QGISbindirectory is missing fromPATH, or the Visual C++ Redistributables are absent on Windows. Repair the install, or run the OSGeo4W installer in repair mode, to restore native binaries. - Confirm you are debugging the right interpreter. Print
sys.executableat the top of the failing script. When an IDE activates an isolated virtual environment, it can strip the inherited system paths that made your terminal work, so the same code passes in one place and fails in another.
For persistent runtime crashes and silent failures that survive the import — bad arguments to the C++ layer, threading issues inside the Qt event loop — structured logging and step-through inspection are the next tools to reach for. The parent Debugging PyQGIS Scripts guide covers isolating stack traces and validating environment state before you deploy to a production pipeline.
Conclusion
PyQGIS import errors almost always reduce to one of three mismatches: the interpreter cannot find the bindings (sys.path), it cannot find their compiled dependencies (PATH / QGIS_PREFIX_PATH), or the Python and QGIS versions are incompatible. Inject the three path variables before any qgis import, match your external Python minor version to the QGIS build, and keep the PyPI qgis stub out of your environment. When manual mapping stays fragile — CI runners, locked-down machines, tangled IDE virtual environments — fall back to the bundled python-qgis wrapper or qgis_process rather than fighting PATH.
Frequently Asked Questions
Why does import qgis fail outside QGIS but work fine in the Python Console?
The QGIS Python Console runs inside the application, which has already set QGIS_PREFIX_PATH, configured PATH, and added the bundled Python libraries to sys.path. An external interpreter inherits none of that, so you must inject those paths manually before importing any qgis module.
What does ImportError: DLL load failed while importing _core actually mean?
It means Python found the qgis package but could not load its compiled C++ dependencies (GDAL, PROJ, Qt). On Windows this is usually a missing QGIS bin directory on PATH or absent Visual C++ Redistributables; repairing the QGIS install via the OSGeo4W installer often resolves it.
Do I need to match my external Python version to QGIS exactly?
You must match the minor version, because the bindings are compiled against a specific Python ABI. QGIS 3.34 and later (including 3.44 LTR) ship Python 3.12, while QGIS 3.28 shipped Python 3.9, so a 3.11 interpreter will raise ImportError against a 3.12 QGIS. Patch versions within the same minor release are fine.
Should I install the qgis package from PyPI to fix the import?
No. The PyPI qgis package is a documentation stub that shadows the real bindings and makes the problem worse. If pip list shows a qgis package in your environment, run pip uninstall qgis and rely on QGIS's bundled libraries via path injection or a --system-site-packages virtual environment.
Why is PYTHONHOME deliberately left unset in the fix?
Setting PYTHONHOME forces the interpreter to look for its standard library in the QGIS prefix, which breaks virtual environments and can trigger silent crashes. Setting only QGIS_PREFIX_PATH, PATH, and sys.path is enough to load the bindings without disturbing the interpreter's own runtime.
Related
- Debugging PyQGIS Scripts — parent guide to the full diagnostic workflow
- Running Python Scripts Outside QGIS Desktop
- How to Install QGIS Python Bindings on Windows