[{"data":1,"prerenderedAt":1414},["ShallowReactive",2],{"doc:\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Ffixing-pyqgis-module-import-errors":3},{"id":4,"title":5,"body":6,"description":1403,"extension":1404,"meta":1405,"navigation":405,"path":1410,"seo":1411,"stem":1412,"__hash__":1413},"docs\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Ffixing-pyqgis-module-import-errors\u002Findex.md","Fixing PyQGIS Module Import Errors",{"type":7,"value":8,"toc":1390},"minimark",[9,13,34,53,65,289,294,297,349,353,371,687,702,710,715,721,816,826,830,833,964,968,971,1071,1074,1078,1081,1124,1128,1135,1231,1237,1241,1269,1273,1294,1312,1321,1346,1364,1368,1386],[10,11,5],"h1",{"id":12},"fixing-pyqgis-module-import-errors",[14,15,16],"p",{},[17,18,19,24,25,24,29,33],"em",{},[20,21,23],"a",{"href":22},"\u002F","Home"," → ",[20,26,28],{"href":27},"\u002Fpyqgis-fundamentals-environment-setup\u002F","PyQGIS Fundamentals & Environment Setup",[20,30,32],{"href":31},"\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002F","Debugging PyQGIS Scripts"," → Fixing Module Import Errors",[14,35,36,40,41,44,45,48,49,52],{},[37,38,39],"code",{},"ModuleNotFoundError: No module named 'qgis'"," and ",[37,42,43],{},"ImportError: DLL load failed while importing _core"," are the two errors you hit the moment you try to ",[37,46,47],{},"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 ",[37,50,51],{},"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.",[14,54,55,56,60,61,64],{},"Import failures are the first wall most people hit when they move from the ",[20,57,59],{"href":58},"\u002Fpyqgis-fundamentals-environment-setup\u002Fqgis-python-console-basics\u002F","QGIS Python Console"," to real scripts, so it is worth understanding ",[17,62,63],{},"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.",[66,67,72,76,80,98,105,115,124,129,137,141,151,154,158,164,172,177,180,184,187,191,195,200,206,210,214,218,223,227,231,235,239,241,243,247,251,254,256,259,261,264,266,268,270,272,274,277,281,285],"svg",{"viewBox":68,"role":69,"ariaLabel":70,"xmlns":71},"0 0 800 520","img","One external Python interpreter fails to import qgis while QGIS_PREFIX_PATH, PATH and sys.path are unset, but the same interpreter succeeds once those three paths are injected and then reaches QgsApplication.initQgis","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[73,74,75],"title",{},"Why import qgis fails outside QGIS, and the fix",[77,78,79],"desc",{},"A before-and-after comparison. The same external interpreter — VS Code, PyCharm, or a terminal — feeds two paths. On the left, QGIS_PREFIX_PATH is unset, PATH has no QGIS bin, and sys.path is missing the python directory, so import qgis.core raises ModuleNotFoundError or a DLL load failure. On the right, sys.path.insert, an updated PATH, and QGIS_PREFIX_PATH are set first, so import qgis.core succeeds and QgsApplication.initQgis leaves PyQGIS ready.",[81,82,83],"defs",{},[84,85,93],"marker",{"id":86,"viewBox":87,"refX":88,"refY":89,"markerWidth":90,"markerHeight":90,"orient":91,"markerUnits":92},"imp-arrow","0 0 10 10","9","5","7","auto","strokeWidth",[94,95],"path",{"d":96,"fill":97},"M0,0 L10,5 L0,10 z","#2f3b35",[99,100],"rect",{"x":101,"y":101,"width":102,"height":103,"fill":104},"0","800","520","#f6f3ea",[99,106],{"x":107,"y":108,"width":109,"height":110,"rx":111,"fill":112,"stroke":113,"style":114},"280","18","240","56","8","#fffdf7","#2563eb","stroke-width:2.5",[116,117,123],"text",{"x":118,"y":119,"style":120,"fill":121,"textAnchor":122},"400","42","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Same external interpreter",[116,125,128],{"x":118,"y":126,"style":127,"fill":97,"textAnchor":122},"61","text-anchor:middle;font-size:12px;font-family:sans-serif","VS Code · PyCharm · terminal",[130,131],"line",{"x1":132,"y1":133,"x2":134,"y2":135,"stroke":97,"style":136},"358","74","212","98","stroke-width:2.5;marker-end:url(#imp-arrow)",[130,138],{"x1":139,"y1":133,"x2":140,"y2":135,"stroke":97,"style":136},"442","588",[99,142],{"x":143,"y":144,"width":145,"height":146,"rx":147,"fill":148,"stroke":149,"style":150},"30","100","350","398","10","none","#b91c1c","stroke-width:1.5;stroke-dasharray:6 5",[99,152],{"x":143,"y":144,"width":145,"height":153,"rx":147,"fill":149},"32",[99,155],{"x":143,"y":156,"width":145,"height":157,"fill":149},"116","16",[116,159,163],{"x":160,"y":161,"style":162,"fill":112,"textAnchor":122},"205","121","text-anchor:middle;font-size:13px;font-weight:bold;font-family:sans-serif","Before — environment not wired",[99,165],{"x":166,"y":167,"width":168,"height":169,"rx":170,"fill":112,"stroke":149,"style":171},"48","148","314","36","6","stroke-width:2",[116,173,176],{"x":160,"y":174,"style":175,"fill":121,"textAnchor":122},"171","text-anchor:middle;font-size:12px;font-family:monospace","QGIS_PREFIX_PATH — unset",[99,178],{"x":166,"y":179,"width":168,"height":169,"rx":170,"fill":112,"stroke":149,"style":171},"192",[116,181,183],{"x":160,"y":182,"style":175,"fill":121,"textAnchor":122},"215","PATH — no QGIS \\bin",[99,185],{"x":166,"y":186,"width":168,"height":169,"rx":170,"fill":112,"stroke":149,"style":171},"236",[116,188,190],{"x":160,"y":189,"style":175,"fill":121,"textAnchor":122},"259","sys.path — missing \\python",[130,192],{"x1":160,"y1":193,"x2":160,"y2":194,"stroke":97,"style":136},"272","298",[99,196],{"x":166,"y":197,"width":168,"height":198,"rx":170,"fill":199},"300","38","#26322d",[116,201,205],{"x":160,"y":202,"style":203,"fill":204,"textAnchor":122},"324","text-anchor:middle;font-size:13px;font-family:monospace","#d9f99d","import qgis.core",[130,207],{"x1":160,"y1":208,"x2":160,"y2":209,"stroke":97,"style":136},"338","362",[99,211],{"x":166,"y":212,"width":168,"height":213,"rx":170,"fill":112,"stroke":149,"style":114},"364","118",[116,215,217],{"x":160,"y":216,"style":162,"fill":149,"textAnchor":122},"390","Import fails",[116,219,222],{"x":160,"y":220,"style":221,"fill":121,"textAnchor":122},"415","text-anchor:middle;font-size:11.5px;font-family:monospace","ModuleNotFoundError:",[116,224,226],{"x":160,"y":225,"style":221,"fill":121,"textAnchor":122},"432","No module named 'qgis'",[116,228,230],{"x":160,"y":229,"style":221,"fill":121,"textAnchor":122},"457","ImportError: DLL load",[116,232,234],{"x":160,"y":233,"style":221,"fill":121,"textAnchor":122},"474","failed importing _core",[99,236],{"x":237,"y":144,"width":145,"height":146,"rx":147,"fill":148,"stroke":238,"style":150},"420","#15803d",[99,240],{"x":237,"y":144,"width":145,"height":153,"rx":147,"fill":238},[99,242],{"x":237,"y":156,"width":145,"height":157,"fill":238},[116,244,246],{"x":245,"y":161,"style":162,"fill":112,"textAnchor":122},"595","After — paths injected first",[99,248],{"x":249,"y":167,"width":168,"height":169,"rx":170,"fill":112,"stroke":250,"style":171},"438","#0f766e",[116,252,253],{"x":245,"y":174,"style":175,"fill":121,"textAnchor":122},"sys.path.insert(0, …\u002Fpython)",[99,255],{"x":249,"y":179,"width":168,"height":169,"rx":170,"fill":112,"stroke":250,"style":171},[116,257,258],{"x":245,"y":182,"style":175,"fill":121,"textAnchor":122},"os.environ PATH += …\u002Fbin",[99,260],{"x":249,"y":186,"width":168,"height":169,"rx":170,"fill":112,"stroke":250,"style":171},[116,262,263],{"x":245,"y":189,"style":175,"fill":121,"textAnchor":122},"QGIS_PREFIX_PATH = prefix",[130,265],{"x1":245,"y1":193,"x2":245,"y2":194,"stroke":97,"style":136},[99,267],{"x":249,"y":197,"width":168,"height":198,"rx":170,"fill":199},[116,269,205],{"x":245,"y":202,"style":203,"fill":204,"textAnchor":122},[130,271],{"x1":245,"y1":208,"x2":245,"y2":209,"stroke":97,"style":136},[99,273],{"x":249,"y":212,"width":168,"height":213,"rx":170,"fill":112,"stroke":238,"style":114},[116,275,276],{"x":245,"y":216,"style":162,"fill":238,"textAnchor":122},"Import succeeds",[116,278,280],{"x":245,"y":279,"style":175,"fill":121,"textAnchor":122},"418","QgsApplication",[116,282,284],{"x":245,"y":283,"style":175,"fill":121,"textAnchor":122},"435",".initQgis()",[116,286,288],{"x":245,"y":287,"style":162,"fill":121,"textAnchor":122},"464","PyQGIS ready",[290,291,293],"h2",{"id":292},"prerequisites","Prerequisites",[14,295,296],{},"Before applying any fix, confirm the following so you are debugging the right layer of the problem:",[298,299,300,308,314,324],"ul",{},[301,302,303,307],"li",{},[304,305,306],"strong",{},"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.",[301,309,310,313],{},[304,311,312],{},"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.",[301,315,316,319,320,323],{},[304,317,318],{},"A 64-bit interpreter."," Modern PyQGIS is strictly 64-bit; a 32-bit Python will never load ",[37,321,322],{},"_core",".",[301,325,326,329,330,333,334,337,338,341,342,344,345,348],{},[304,327,328],{},"The ability to inspect your environment."," You should be able to print ",[37,331,332],{},"sys.executable",", ",[37,335,336],{},"sys.version",", and ",[37,339,340],{},"os.environ"," from 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 ",[20,343,28],{"href":27}," reference explains how interpreter inheritance and ",[37,346,347],{},"site-packages"," resolution work.",[290,350,352],{"id":351},"the-fix-manual-path-injection","The Fix: Manual Path Injection",[14,354,355,356,359,360,363,364,40,367,370],{},"When running scripts externally, inject QGIS paths ",[17,357,358],{},"before"," any ",[37,361,362],{},"qgis"," import. Import order matters — the compiled libraries are resolved against ",[37,365,366],{},"PATH",[37,368,369],{},"sys.path"," at import time, so setting these variables afterwards has no effect. Place this block at the very top of your script:",[372,373,378],"pre",{"className":374,"code":375,"language":376,"meta":377,"style":377},"language-python shiki shiki-themes github-dark","import sys\nimport os\n\n# Update to match your exact QGIS installation\nQGIS_PREFIX = r\"C:\\Program Files\\QGIS 3.44\\apps\\qgis\"  # Windows Standalone\n# QGIS_PREFIX = \"\u002FApplications\u002FQGIS.app\u002FContents\u002FMacOS\"  # macOS\n# QGIS_PREFIX = \"\u002Fusr\"                                    # Linux (Debian\u002FUbuntu)\n\n# Inject QGIS Python paths (order matters)\nsys.path.insert(0, os.path.join(QGIS_PREFIX, \"python\"))\nsys.path.insert(0, os.path.join(QGIS_PREFIX, \"python\", \"plugins\"))\n\n# Set mandatory environment variables\nos.environ[\"QGIS_PREFIX_PATH\"] = QGIS_PREFIX\nos.environ[\"PATH\"] = os.path.join(QGIS_PREFIX, \"bin\") + os.pathsep + os.environ.get(\"PATH\", \"\")\n\n# Initialize QGIS application context\nfrom qgis.core import QgsApplication\nQgsApplication.setPrefixPath(QGIS_PREFIX, True)\nqgs = QgsApplication([], False)\nqgs.initQgis()\nprint(\"PyQGIS environment loaded successfully.\")\n","python","",[37,379,380,392,400,407,414,470,476,482,487,493,514,536,541,547,565,611,616,622,636,651,667,673],{"__ignoreMap":377},[381,382,384,388],"span",{"class":130,"line":383},1,[381,385,387],{"class":386},"snl16","import",[381,389,391],{"class":390},"s95oV"," sys\n",[381,393,395,397],{"class":130,"line":394},2,[381,396,387],{"class":386},[381,398,399],{"class":390}," os\n",[381,401,403],{"class":130,"line":402},3,[381,404,406],{"emptyLinePlaceholder":405},true,"\n",[381,408,410],{"class":130,"line":409},4,[381,411,413],{"class":412},"sjoCn","# Update to match your exact QGIS installation\n",[381,415,417,421,424,427,431,435,439,442,445,448,450,453,456,459,462,465,467],{"class":130,"line":416},5,[381,418,420],{"class":419},"sDLfK","QGIS_PREFIX",[381,422,423],{"class":386}," =",[381,425,426],{"class":386}," r",[381,428,430],{"class":429},"sU2Wk","\"",[381,432,434],{"class":433},"sns5M","C:",[381,436,438],{"class":437},"sRjNt","\\P",[381,440,441],{"class":433},"rogram Files",[381,443,444],{"class":437},"\\Q",[381,446,447],{"class":433},"GIS 3",[381,449,323],{"class":419},[381,451,452],{"class":433},"44",[381,454,455],{"class":437},"\\a",[381,457,458],{"class":433},"pps",[381,460,461],{"class":437},"\\q",[381,463,464],{"class":433},"gis",[381,466,430],{"class":429},[381,468,469],{"class":412},"  # Windows Standalone\n",[381,471,473],{"class":130,"line":472},6,[381,474,475],{"class":412},"# QGIS_PREFIX = \"\u002FApplications\u002FQGIS.app\u002FContents\u002FMacOS\"  # macOS\n",[381,477,479],{"class":130,"line":478},7,[381,480,481],{"class":412},"# QGIS_PREFIX = \"\u002Fusr\"                                    # Linux (Debian\u002FUbuntu)\n",[381,483,485],{"class":130,"line":484},8,[381,486,406],{"emptyLinePlaceholder":405},[381,488,490],{"class":130,"line":489},9,[381,491,492],{"class":412},"# Inject QGIS Python paths (order matters)\n",[381,494,496,499,501,504,506,508,511],{"class":130,"line":495},10,[381,497,498],{"class":390},"sys.path.insert(",[381,500,101],{"class":419},[381,502,503],{"class":390},", os.path.join(",[381,505,420],{"class":419},[381,507,333],{"class":390},[381,509,510],{"class":429},"\"python\"",[381,512,513],{"class":390},"))\n",[381,515,517,519,521,523,525,527,529,531,534],{"class":130,"line":516},11,[381,518,498],{"class":390},[381,520,101],{"class":419},[381,522,503],{"class":390},[381,524,420],{"class":419},[381,526,333],{"class":390},[381,528,510],{"class":429},[381,530,333],{"class":390},[381,532,533],{"class":429},"\"plugins\"",[381,535,513],{"class":390},[381,537,539],{"class":130,"line":538},12,[381,540,406],{"emptyLinePlaceholder":405},[381,542,544],{"class":130,"line":543},13,[381,545,546],{"class":412},"# Set mandatory environment variables\n",[381,548,550,553,556,559,562],{"class":130,"line":549},14,[381,551,552],{"class":390},"os.environ[",[381,554,555],{"class":429},"\"QGIS_PREFIX_PATH\"",[381,557,558],{"class":390},"] ",[381,560,561],{"class":386},"=",[381,563,564],{"class":419}," QGIS_PREFIX\n",[381,566,568,570,573,575,577,580,582,584,587,590,593,596,598,601,603,605,608],{"class":130,"line":567},15,[381,569,552],{"class":390},[381,571,572],{"class":429},"\"PATH\"",[381,574,558],{"class":390},[381,576,561],{"class":386},[381,578,579],{"class":390}," os.path.join(",[381,581,420],{"class":419},[381,583,333],{"class":390},[381,585,586],{"class":429},"\"bin\"",[381,588,589],{"class":390},") ",[381,591,592],{"class":386},"+",[381,594,595],{"class":390}," os.pathsep ",[381,597,592],{"class":386},[381,599,600],{"class":390}," os.environ.get(",[381,602,572],{"class":429},[381,604,333],{"class":390},[381,606,607],{"class":429},"\"\"",[381,609,610],{"class":390},")\n",[381,612,614],{"class":130,"line":613},16,[381,615,406],{"emptyLinePlaceholder":405},[381,617,619],{"class":130,"line":618},17,[381,620,621],{"class":412},"# Initialize QGIS application context\n",[381,623,625,628,631,633],{"class":130,"line":624},18,[381,626,627],{"class":386},"from",[381,629,630],{"class":390}," qgis.core ",[381,632,387],{"class":386},[381,634,635],{"class":390}," QgsApplication\n",[381,637,639,642,644,646,649],{"class":130,"line":638},19,[381,640,641],{"class":390},"QgsApplication.setPrefixPath(",[381,643,420],{"class":419},[381,645,333],{"class":390},[381,647,648],{"class":419},"True",[381,650,610],{"class":390},[381,652,654,657,659,662,665],{"class":130,"line":653},20,[381,655,656],{"class":390},"qgs ",[381,658,561],{"class":386},[381,660,661],{"class":390}," QgsApplication([], ",[381,663,664],{"class":419},"False",[381,666,610],{"class":390},[381,668,670],{"class":130,"line":669},21,[381,671,672],{"class":390},"qgs.initQgis()\n",[381,674,676,679,682,685],{"class":130,"line":675},22,[381,677,678],{"class":419},"print",[381,680,681],{"class":390},"(",[381,683,684],{"class":429},"\"PyQGIS environment loaded successfully.\"",[381,686,610],{"class":390},[14,688,689,690,693,694,333,697,337,699,701],{},"Note that ",[37,691,692],{},"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 ",[37,695,696],{},"QGIS_PREFIX_PATH",[37,698,366],{},[37,700,369],{}," is enough to load the bindings without disturbing the interpreter's own runtime.",[14,703,704,705,709],{},"This bootstrap is the same one used when ",[20,706,708],{"href":707},"\u002Fpyqgis-fundamentals-environment-setup\u002Fvirtual-environments-for-gis\u002Frunning-python-scripts-outside-qgis-desktop\u002F","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.",[711,712,714],"h3",{"id":713},"os-specific-path-differences","OS-specific path differences",[14,716,717,718,720],{},"The one line you almost always get wrong is ",[37,719,420],{},". The correct value depends on how QGIS was installed:",[722,723,724,740],"table",{},[725,726,727],"thead",{},[728,729,730,734,737],"tr",{},[731,732,733],"th",{},"Install type",[731,735,736],{},"Typical QGIS_PREFIX",[731,738,739],{},"Notes",[741,742,743,761,776,800],"tbody",{},[728,744,745,749,754],{},[746,747,748],"td",{},"Windows Standalone",[746,750,751],{},[37,752,753],{},"C:\\Program Files\\QGIS 3.44\\apps\\qgis",[746,755,756,757,760],{},"Use ",[37,758,759],{},"apps\\qgis-ltr"," for an LTR build",[728,762,763,766,771],{},[746,764,765],{},"Windows OSGeo4W",[746,767,768],{},[37,769,770],{},"C:\\OSGeo4W\\apps\\qgis-ltr",[746,772,773,774],{},"Prefer the OSGeo4W shell for wiring ",[37,775,366],{},[728,777,778,781,786],{},[746,779,780],{},"macOS bundle",[746,782,783],{},[37,784,785],{},"\u002FApplications\u002FQGIS.app\u002FContents\u002FMacOS",[746,787,788,789,333,792,795,796,799],{},"Point at ",[37,790,791],{},"Contents\u002FMacOS",[304,793,794],{},"not"," the ",[37,797,798],{},".app"," root",[728,801,802,805,810],{},[746,803,804],{},"Linux (package)",[746,806,807],{},[37,808,809],{},"\u002Fusr",[746,811,812,813],{},"Symlinks are usually already on the path; injection is only needed inside an isolated ",[37,814,815],{},"venv",[14,817,818,819,821,822,825],{},"On Linux, distribution packages typically register the bindings system-wide, so ",[37,820,47],{}," works without any injection unless you have created an isolated virtual environment. In that case, create the environment with ",[37,823,824],{},"--system-site-packages"," so it can see the packaged bindings.",[290,827,829],{"id":828},"where-python-looks-and-why-it-misses","Where Python looks, and why it misses",[14,831,832],{},"An import error is almost always a path problem, and seeing the search order makes it obvious which of the three fixes applies.",[14,834,835],{},[66,836,839,842,845,849,858,863,869,875,913,917,923,928,932,936,940,943,947,950,957,960],{"viewBox":837,"role":69,"ariaLabel":838,"xmlns":71},"0 0 760 248","Python's module search path with the QGIS python directory missing, and the three ways of inserting it: PYTHONPATH, sys.path and a pth file",[73,840,841],{},"The search path, and three ways to add QGIS to it",[77,843,844],{},"Python searches the script directory, then PYTHONPATH entries, then the standard library, then site-packages. The QGIS python directory is in none of those by default. It can be added by setting PYTHONPATH before launch, by inserting into sys.path at runtime, or by dropping a pth file into site-packages.",[99,846],{"x":101,"y":101,"width":847,"height":848,"fill":104},"760","248",[81,850,851],{},[84,852,855],{"id":853,"viewBox":87,"refX":111,"refY":89,"markerWidth":90,"markerHeight":90,"orient":854},"impPathArrow","auto-start-reverse",[94,856],{"d":857,"fill":250},"M0 0 L10 5 L0 10 z",[116,859,862],{"x":860,"y":861,"style":120,"fill":121,"textAnchor":122},"380","26","import qgis fails because none of these contain it",[99,864],{"x":157,"y":865,"width":866,"height":867,"rx":147,"fill":112,"stroke":868,"style":114},"46","330","186","#59645f",[116,870,874],{"x":871,"y":872,"style":873,"fill":121,"textAnchor":122},"181","70","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","sys.path, in order",[876,877,879,886,891,895,899,901,905,909],"g",{"style":878},"font-size:11px;font-family:monospace",[99,880],{"x":881,"y":882,"width":883,"height":861,"rx":884,"fill":112,"stroke":868,"style":885},"40","84","282","4","stroke-width:1.3",[116,887,890],{"x":888,"y":889,"fill":97},"52","102","1  the script's own directory",[99,892],{"x":881,"y":156,"width":883,"height":861,"rx":884,"fill":113,"fillOpacity":893,"stroke":113,"style":894},0.15,"stroke-width:1.6",[116,896,898],{"x":888,"y":897,"fill":97},"134","2  $PYTHONPATH entries",[99,900],{"x":881,"y":167,"width":883,"height":861,"rx":884,"fill":112,"stroke":868,"style":885},[116,902,904],{"x":888,"y":903,"fill":97},"166","3  the standard library",[99,906],{"x":881,"y":907,"width":883,"height":861,"rx":884,"fill":238,"fillOpacity":908,"stroke":238,"style":894},"180",0.14,[116,910,912],{"x":888,"y":911,"fill":97},"198","4  site-packages",[99,914],{"x":915,"y":865,"width":916,"height":888,"rx":111,"fill":112,"stroke":113,"style":171},"416","328",[116,918,922],{"x":919,"y":920,"style":921,"fill":113},"436","68","font-size:11px;font-weight:bold;font-family:sans-serif","set PYTHONPATH before launch",[116,924,927],{"x":919,"y":925,"style":926,"fill":97},"87","font-size:10.5px;font-family:sans-serif","works for every script · the portable fix",[99,929],{"x":915,"y":930,"width":916,"height":888,"rx":111,"fill":112,"stroke":931,"style":171},"110","#b45309",[116,933,935],{"x":919,"y":934,"style":921,"fill":931},"132","sys.path.insert(0, …)",[116,937,939],{"x":919,"y":938,"style":926,"fill":97},"151","works in this script only · hard-codes a path",[99,941],{"x":915,"y":942,"width":916,"height":888,"rx":111,"fill":112,"stroke":238,"style":171},"174",[116,944,946],{"x":919,"y":945,"style":921,"fill":238},"196","a .pth file in site-packages",[116,948,949],{"x":919,"y":182,"style":926,"fill":97},"works for one environment · invisible later",[130,951],{"x1":952,"y1":953,"x2":954,"y2":955,"stroke":250,"style":956},"346","129","412","72","stroke-width:2;marker-end:url(#impPathArrow)",[130,958],{"x1":952,"y1":953,"x2":954,"y2":959,"stroke":250,"style":956},"136",[130,961],{"x1":952,"y1":962,"x2":954,"y2":963,"stroke":250,"style":956},"193","200",[290,965,967],{"id":966},"interpreter-mismatch-is-a-different-failure","Interpreter mismatch is a different failure",[14,969,970],{},"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.",[14,972,973],{},[66,974,977,980,983,985,992,995,1001,1006,1011,1014,1019,1022,1025,1028,1032,1039,1044,1048,1054,1057,1060,1063,1068],{"viewBox":975,"role":69,"ariaLabel":976,"xmlns":71},"0 0 760 236","A matching interpreter importing the QGIS bindings successfully, contrasted with a different Python version producing a binary incompatibility error",[73,978,979],{},"Matching versus mismatched interpreter",[77,981,982],{},"QGIS ships compiled bindings built against one specific Python version. The bundled interpreter of that version imports them successfully. A separately installed interpreter of a different version finds the files but cannot load them, producing an ImportError about a binary module rather than a missing one.",[99,984],{"x":101,"y":101,"width":847,"height":186,"fill":104},[81,986,987],{},[84,988,990],{"id":989,"viewBox":87,"refX":111,"refY":89,"markerWidth":90,"markerHeight":90,"orient":854},"impVerArrow",[94,991],{"d":857,"fill":97},[116,993,994],{"x":860,"y":861,"style":120,"fill":121,"textAnchor":122},"The bindings are compiled — the version has to match exactly",[99,996],{"x":997,"y":998,"width":999,"height":1000,"rx":147,"fill":199,"stroke":250,"style":114},"288","94","184","66",[116,1002,1005],{"x":860,"y":1003,"style":1004,"fill":204,"textAnchor":122},"122","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif","qgis._core.so",[116,1007,1010],{"x":860,"y":1008,"style":1009,"fill":204,"textAnchor":122},"144","text-anchor:middle;font-size:10.5px;font-family:sans-serif","built for Python 3.12",[99,1012],{"x":157,"y":888,"width":1013,"height":1000,"rx":147,"fill":112,"stroke":238,"style":114},"228",[116,1015,1018],{"x":1016,"y":1017,"style":1004,"fill":238,"textAnchor":122},"130","80","the bundled python3.12",[116,1020,1021],{"x":1016,"y":889,"style":1009,"fill":97,"textAnchor":122},"imports cleanly",[99,1023],{"x":157,"y":1024,"width":1013,"height":1000,"rx":147,"fill":112,"stroke":149,"style":114},"138",[116,1026,1027],{"x":1016,"y":903,"style":1004,"fill":149,"textAnchor":122},"a separate python3.10",[116,1029,1031],{"x":1016,"y":1030,"style":1009,"fill":97,"textAnchor":122},"188","finds it, cannot load it",[130,1033],{"x1":1034,"y1":1035,"x2":1036,"y2":1037,"stroke":238,"style":1038},"244","88","284","112","stroke-width:2.5;marker-end:url(#impVerArrow)",[130,1040],{"x1":1034,"y1":1041,"x2":1036,"y2":1042,"stroke":149,"style":1043},"170","145","stroke-width:2.5;stroke-dasharray:5 4;marker-end:url(#impVerArrow)",[99,1045],{"x":1046,"y":1047,"width":1013,"height":888,"rx":111,"fill":112,"stroke":238,"style":171},"516","60",[116,1049,1053],{"x":1050,"y":1051,"style":1052,"fill":238,"textAnchor":122},"630","92","text-anchor:middle;font-size:11px;font-family:sans-serif","from qgis.core import QgsProject",[99,1055],{"x":1046,"y":1056,"width":1013,"height":1000,"rx":111,"fill":112,"stroke":149,"style":171},"140",[116,1058,1059],{"x":1050,"y":903,"style":1009,"fill":97,"textAnchor":122},"ImportError: undefined symbol",[116,1061,1062],{"x":1050,"y":867,"style":1009,"fill":149,"textAnchor":122},"not \"no module named\"",[130,1064],{"x1":1065,"y1":1037,"x2":1066,"y2":1035,"stroke":238,"style":1067},"472","512","stroke-width:2;marker-end:url(#impVerArrow)",[130,1069],{"x1":1065,"y1":1042,"x2":1066,"y2":1070,"stroke":149,"style":1067},"168",[14,1072,1073],{},"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.",[290,1075,1077],{"id":1076},"qgis-version-compatibility-notes","QGIS-version compatibility notes",[14,1079,1080],{},"Import errors are frequently version-mismatch errors in disguise. Keep these rules in mind:",[298,1082,1083,1093,1099,1105,1114],{},[301,1084,1085,1088,1089,1092],{},[304,1086,1087],{},"Match the minor Python version."," Because the bindings are compiled against a specific ABI, a 3.11 interpreter will raise ",[37,1090,1091],{},"ImportError"," against 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.",[301,1094,1095,1098],{},[304,1096,1097],{},"QGIS 3.34+ \u002F 3.44 LTR → Python 3.12."," These are the current targets and what the code above assumes.",[301,1100,1101,1104],{},[304,1102,1103],{},"QGIS 3.28 LTR → Python 3.9."," If you are pinned to this older LTR, your external interpreter must also be 3.9.",[301,1106,1107,1110,1111,1113],{},[304,1108,1109],{},"Architecture must match."," A 64-bit QGIS needs a 64-bit interpreter; the mismatch surfaces as a failure during ",[37,1112,280],{}," initialization rather than at import.",[301,1115,1116,1119,1120,1123],{},[304,1117,1118],{},"API surface drifts between releases."," Even once the import succeeds, classes added in later releases will raise ",[37,1121,1122],{},"AttributeError"," on an older QGIS. Pin your example code to the LTR you actually run.",[290,1125,1127],{"id":1126},"troubleshooting","Troubleshooting",[14,1129,1130,1131,1134],{},"If path injection still throws ",[37,1132,1133],{},"ImportError: cannot import name 'QgsApplication'",", crashes silently, or reports a missing DLL, work through these fallbacks in order.",[1136,1137,1138,1165,1182,1202,1222],"ol",{},[301,1139,1140,1143,1144],{},[304,1141,1142],{},"Execute via the QGIS Python wrapper."," Bypass the external interpreter entirely by calling the bundled executable, which has every path pre-configured:\n",[298,1145,1146,1152,1158],{},[301,1147,1148,1149],{},"Windows: ",[37,1150,1151],{},"\"C:\\Program Files\\QGIS 3.44\\bin\\python-qgis.bat\" your_script.py",[301,1153,1154,1155],{},"macOS: ",[37,1156,1157],{},"\u002FApplications\u002FQGIS.app\u002FContents\u002FMacOS\u002Fbin\u002Fpython3 your_script.py",[301,1159,1160,1161,1164],{},"Linux: ",[37,1162,1163],{},"python3 your_script.py"," after sourcing the QGIS environment shell script",[301,1166,1167,1170,1171,1174,1175,1177,1178,1181],{},[304,1168,1169],{},"Run inside the QGIS Python Console."," Open QGIS → Plugins → Python Console and run ",[37,1172,1173],{},"exec(open('your_script.py').read())",". The console auto-initializes ",[37,1176,280],{}," and loads all ",[37,1179,1180],{},"qgis.*"," namespaces, which is the fastest way to confirm the problem is environmental rather than a bug in your code.",[301,1183,1184,1187,1188,1191,1192,1195,1196,1198,1199,323],{},[304,1185,1186],{},"Remove a conflicting PyPI package."," Run ",[37,1189,1190],{},"pip list | grep qgis"," (macOS\u002FLinux) or ",[37,1193,1194],{},"pip list | findstr qgis"," (Windows). Official PyQGIS bindings are never distributed through PyPI — the PyPI ",[37,1197,362],{}," package is a documentation stub that shadows the real libraries. If you see it, uninstall it immediately with ",[37,1200,1201],{},"pip uninstall qgis",[301,1203,1204,1207,1208,1211,1212,1214,1215,1218,1219,1221],{},[304,1205,1206],{},"Verify the native dependency chain."," PyQGIS depends on compiled C++ libraries (GDAL, PROJ, Qt). ",[37,1209,1210],{},"DLL load failed while importing _core"," means Python found the ",[37,1213,362],{}," package but could not load those dependencies — usually because the QGIS ",[37,1216,1217],{},"bin"," directory is missing from ",[37,1220,366],{},", or the Visual C++ Redistributables are absent on Windows. Repair the install, or run the OSGeo4W installer in repair mode, to restore native binaries.",[301,1223,1224,1227,1228,1230],{},[304,1225,1226],{},"Confirm you are debugging the right interpreter."," Print ",[37,1229,332],{}," at 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.",[14,1232,1233,1234,1236],{},"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 ",[20,1235,32],{"href":31}," guide covers isolating stack traces and validating environment state before you deploy to a production pipeline.",[290,1238,1240],{"id":1239},"conclusion","Conclusion",[14,1242,1243,1244,1246,1247,1249,1250,1252,1253,1255,1256,1258,1259,1262,1263,1266,1267,323],{},"PyQGIS import errors almost always reduce to one of three mismatches: the interpreter cannot find the bindings (",[37,1245,369],{},"), it cannot find their compiled dependencies (",[37,1248,366],{}," \u002F ",[37,1251,696],{},"), or the Python and QGIS versions are incompatible. Inject the three path variables before any ",[37,1254,362],{}," import, match your external Python minor version to the QGIS build, and keep the PyPI ",[37,1257,362],{}," stub out of your environment. When manual mapping stays fragile — CI runners, locked-down machines, tangled IDE virtual environments — fall back to the bundled ",[37,1260,1261],{},"python-qgis"," wrapper or ",[37,1264,1265],{},"qgis_process"," rather than fighting ",[37,1268,366],{},[290,1270,1272],{"id":1271},"frequently-asked-questions","Frequently Asked Questions",[14,1274,1275,1281,1282,1284,1285,1287,1288,1290,1291,1293],{},[304,1276,1277,1278,1280],{},"Why does ",[37,1279,47],{}," fail outside QGIS but work fine in the Python Console?","\nThe QGIS Python Console runs inside the application, which has already set ",[37,1283,696],{},", configured ",[37,1286,366],{},", and added the bundled Python libraries to ",[37,1289,369],{},". An external interpreter inherits none of that, so you must inject those paths manually before importing any ",[37,1292,362],{}," module.",[14,1295,1296,1302,1303,1305,1306,1308,1309,1311],{},[304,1297,1298,1299,1301],{},"What does ",[37,1300,43],{}," actually mean?","\nIt means Python found the ",[37,1304,362],{}," package but could not load its compiled C++ dependencies (GDAL, PROJ, Qt). On Windows this is usually a missing QGIS ",[37,1307,1217],{}," directory on ",[37,1310,366],{}," or absent Visual C++ Redistributables; repairing the QGIS install via the OSGeo4W installer often resolves it.",[14,1313,1314,1317,1318,1320],{},[304,1315,1316],{},"Do I need to match my external Python version to QGIS exactly?","\nYou 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 ",[37,1319,1091],{}," against a 3.12 QGIS. Patch versions within the same minor release are fine.",[14,1322,1323,1329,1330,1332,1333,1336,1337,1339,1340,1342,1343,1345],{},[304,1324,1325,1326,1328],{},"Should I install the ",[37,1327,362],{}," package from PyPI to fix the import?","\nNo. The PyPI ",[37,1331,362],{}," package is a documentation stub that shadows the real bindings and makes the problem worse. If ",[37,1334,1335],{},"pip list"," shows a ",[37,1338,362],{}," package in your environment, run ",[37,1341,1201],{}," and rely on QGIS's bundled libraries via path injection or a ",[37,1344,824],{}," virtual environment.",[14,1347,1348,1354,1355,1357,1358,333,1360,337,1362,701],{},[304,1349,1350,1351,1353],{},"Why is ",[37,1352,692],{}," deliberately left unset in the fix?","\nSetting ",[37,1356,692],{}," forces the interpreter to look for its standard library in the QGIS prefix, which breaks virtual environments and can trigger silent crashes. Setting only ",[37,1359,696],{},[37,1361,366],{},[37,1363,369],{},[290,1365,1367],{"id":1366},"related","Related",[298,1369,1370,1375,1380],{},[301,1371,1372,1374],{},[20,1373,32],{"href":31}," — parent guide to the full diagnostic workflow",[301,1376,1377],{},[20,1378,1379],{"href":707},"Running Python Scripts Outside QGIS Desktop",[301,1381,1382],{},[20,1383,1385],{"href":1384},"\u002Fpyqgis-fundamentals-environment-setup\u002Fqgis-python-console-basics\u002Fhow-to-install-qgis-python-bindings-on-windows\u002F","How to Install QGIS Python Bindings on Windows",[1387,1388,1389],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sns5M, html code.shiki .sns5M{--shiki-default:#DBEDFF}html pre.shiki code .sRjNt, html code.shiki .sRjNt{--shiki-default:#85E89D;--shiki-default-font-weight:bold}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":377,"searchDepth":394,"depth":394,"links":1391},[1392,1393,1396,1397,1398,1399,1400,1401,1402],{"id":292,"depth":394,"text":293},{"id":351,"depth":394,"text":352,"children":1394},[1395],{"id":713,"depth":402,"text":714},{"id":828,"depth":394,"text":829},{"id":966,"depth":394,"text":967},{"id":1076,"depth":394,"text":1077},{"id":1126,"depth":394,"text":1127},{"id":1239,"depth":394,"text":1240},{"id":1271,"depth":394,"text":1272},{"id":1366,"depth":394,"text":1367},"Resolve PyQGIS module import errors caused by missing paths, wrong Python versions, or broken QGIS installs. Step-by-step diagnosis and fixes.","md",{"slug":12,"type":1406,"breadcrumb":1407,"datePublished":1408,"dateModified":1409},"article","Fixing Module Import Errors","2025-02-04","2026-07-18","\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Ffixing-pyqgis-module-import-errors",{"title":5,"description":1403},"pyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Ffixing-pyqgis-module-import-errors\u002Findex","1BHL2Fmby1i7xrubB7LaA5fZqwzMHmUGmBg6b7dMSEE",1787823360562]