[{"data":1,"prerenderedAt":2272},["ShallowReactive",2],{"doc:\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Fdebug-invalid-layer-loading-pyqgis":3},{"id":4,"title":5,"body":6,"description":2261,"extension":2262,"meta":2263,"navigation":280,"path":2268,"seo":2269,"stem":2270,"__hash__":2271},"docs\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Fdebug-invalid-layer-loading-pyqgis\u002Findex.md","Debug Invalid Layer Loading in PyQGIS",{"type":7,"value":8,"toc":2248},"minimark",[9,13,33,42,205,210,241,245,248,468,485,489,492,597,933,958,962,965,1225,1244,1289,1293,1296,1621,1639,1643,1646,2078,2088,2092,2112,2116,2153,2157,2163,2167,2173,2182,2195,2209,2213,2244],[10,11,5],"h1",{"id":12},"debug-invalid-layer-loading-in-pyqgis",[14,15,16,20,21,24,25,28,29,32],"p",{},[17,18,19],"code",{},"QgsVectorLayer(\"\u002Fdata\u002Froads.shp\", \"roads\", \"ogr\")"," never raises. When something is wrong — a typo in the path, a missing driver, a password that expired, a URI with the wrong quoting — it quietly returns a layer object whose ",[17,22,23],{},"isValid()"," is ",[17,26,27],{},"False",", with no features, no fields and no extent. Scripts that skip the check fail three steps later with a confusing error about an empty extent or a ",[17,30,31],{},"None"," provider, and the original reason is gone.",[14,34,35,36,41],{},"This recipe belongs to ",[37,38,40],"a",{"href":39},"\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002F","Debugging PyQGIS Scripts",". It reads the errors QGIS does record, works through the checks in the order that finds problems fastest, asks GDAL directly when QGIS is vague, covers databases and web services, and ends with a loading helper that turns every invalid layer into a clear exception.",[14,43,44],{},[45,46,51,55,59,66,83,92,101,107,113,116,120,124,127,131,135,140,144,148,154,158,166,172,177,181,184,188,192,196,200],"svg",{"viewBox":47,"role":48,"ariaLabel":49,"xmlns":50},"0 0 760 310","img","A diagnostic path for an invalid layer: read layer and provider errors, confirm the provider key exists, confirm the path or URI, open the source with GDAL directly, then check connection and authentication for database and web layers","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[52,53,54],"title",{},"Check in this order",[56,57,58],"desc",{},"Five steps from top to bottom. One, read layer.error and the provider error, which often name the cause. Two, check the provider key is in QgsProviderRegistry.providerList, catching typos like ogr versus OGR. Three, check the path exists and the URI decodes as expected. Four, open the same source with GDAL or OGR directly to get the driver's own error message. Five, for postgres, WFS and other remote sources, test the connection and authentication configuration separately. Most problems are found by step three.",[60,61],"rect",{"x":62,"y":62,"width":63,"height":64,"fill":65},"0","760","310","#f6f3ea",[67,68,69],"defs",{},[70,71,78],"marker",{"id":72,"viewBox":73,"refX":74,"refY":75,"markerWidth":76,"markerHeight":76,"orient":77},"invStepArrow","0 0 10 10","8","5","7","auto-start-reverse",[79,80],"path",{"d":81,"fill":82},"M0 0 L10 5 L0 10 z","#2f3b35",[84,85,91],"text",{"x":86,"y":87,"style":88,"fill":89,"textAnchor":90},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Cheapest checks first",[60,93],{"x":94,"y":95,"width":96,"height":97,"rx":74,"fill":98,"stroke":99,"style":100},"60","48","400","40","#eef7f4","#0f766e","stroke-width:2",[84,102,106],{"x":103,"y":104,"style":105,"fill":82,"textAnchor":90},"260","73","text-anchor:middle;font-size:10.5px;font-family:sans-serif","1 · read layer.error() and the provider error",[108,109],"line",{"x1":103,"y1":110,"x2":103,"y2":111,"stroke":82,"style":112},"88","100","stroke-width:1.6;marker-end:url(#invStepArrow)",[60,114],{"x":94,"y":115,"width":96,"height":97,"rx":74,"fill":98,"stroke":99,"style":100},"104",[84,117,119],{"x":103,"y":118,"style":105,"fill":82,"textAnchor":90},"129","2 · is the provider key real? (ogr, gdal, postgres…)",[108,121],{"x1":103,"y1":122,"x2":103,"y2":123,"stroke":82,"style":112},"144","156",[60,125],{"x":94,"y":126,"width":96,"height":97,"rx":74,"fill":98,"stroke":99,"style":100},"160",[84,128,130],{"x":103,"y":129,"style":105,"fill":82,"textAnchor":90},"185","3 · does the path exist, does the URI decode?",[108,132],{"x1":103,"y1":133,"x2":103,"y2":134,"stroke":82,"style":112},"200","212",[60,136],{"x":94,"y":137,"width":96,"height":97,"rx":74,"fill":138,"stroke":139,"style":100},"216","#eff3ff","#2563eb",[84,141,143],{"x":103,"y":142,"style":105,"fill":82,"textAnchor":90},"241","4 · open it with GDAL\u002FOGR for the driver's message",[108,145],{"x1":103,"y1":146,"x2":103,"y2":147,"stroke":82,"style":112},"256","268",[60,149],{"x":94,"y":150,"width":96,"height":151,"rx":74,"fill":152,"stroke":153,"style":100},"272","30","#fdf2e2","#b45309",[84,155,157],{"x":103,"y":156,"style":105,"fill":82,"textAnchor":90},"292","5 · remote: test connection and auth config",[60,159],{"x":160,"y":95,"width":161,"height":162,"rx":163,"fill":164,"stroke":165,"style":100},"500","236","254","10","#fffdf7","#59645f",[84,167,171],{"x":168,"y":169,"style":170,"fill":89,"textAnchor":90},"618","74","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","usual causes found",[84,173,176],{"x":168,"y":174,"style":175,"fill":82,"textAnchor":90},"108","text-anchor:middle;font-size:10px;font-family:sans-serif","wrong provider key",[84,178,180],{"x":168,"y":179,"style":175,"fill":82,"textAnchor":90},"132","relative path, wrong cwd",[84,182,183],{"x":168,"y":123,"style":175,"fill":82,"textAnchor":90},"wrong layername in a gpkg",[84,185,187],{"x":168,"y":186,"style":175,"fill":82,"textAnchor":90},"180","file locked or partial copy",[84,189,191],{"x":168,"y":190,"style":175,"fill":82,"textAnchor":90},"204","driver missing in this build",[84,193,195],{"x":168,"y":194,"style":175,"fill":82,"textAnchor":90},"228","expired credentials",[84,197,199],{"x":168,"y":198,"style":175,"fill":82,"textAnchor":90},"252","proxy or SSL blocks",[84,201,204],{"x":168,"y":202,"style":203,"fill":165,"textAnchor":90},"282","text-anchor:middle;font-size:9.5px;font-family:sans-serif","most by step 3",[206,207,209],"h2",{"id":208},"prerequisites","Prerequisites",[211,212,213,221,224],"ul",{},[214,215,216,220],"li",{},[217,218,219],"strong",{},"QGIS 3.40 LTR"," or newer, or the QGIS 4 series.",[214,222,223],{},"A layer that loads as invalid, and the exact source string and provider key that produced it.",[214,225,226,227,230,231,235,236,240],{},"For standalone scripts, a correctly initialised ",[17,228,229],{},"QgsApplication",". An uninitialised application makes ",[232,233,234],"em",{},"every"," layer invalid, which is a different problem covered in ",[37,237,239],{"href":238},"\u002Fpyqgis-fundamentals-environment-setup\u002Fvirtual-environments-for-gis\u002Frunning-python-scripts-outside-qgis-desktop\u002F","running Python scripts outside QGIS Desktop",".",[206,242,244],{"id":243},"read-the-errors-qgis-recorded","Read the errors QGIS recorded",[14,246,247],{},"The layer and its provider both keep an error object. They are often empty, but when they are not, they name the cause directly and save the rest of the investigation.",[249,250,255],"pre",{"className":251,"code":252,"language":253,"meta":254,"style":254},"language-python shiki shiki-themes github-dark","from qgis.core import QgsVectorLayer, QgsRasterLayer, QgsApplication\n\nsource = \"\u002Fdata\u002Fwork\u002Fsurvey_2026.gpkg|layername=trees\"\nlayer = QgsVectorLayer(source, \"trees\", \"ogr\")\n\nprint(\"valid:\", layer.isValid())\nif not layer.isValid():\n    print(\"layer error:   \", layer.error().summary() or \"(empty)\")\n    provider = layer.dataProvider()\n    if provider is None:\n        print(\"no provider was created — check the provider key\")\n    else:\n        print(\"provider error:\", provider.error().message() or \"(empty)\")\n        print(\"provider valid:\", provider.isValid())\n    print(\"prefix path:   \", QgsApplication.prefixPath())\n","python","",[17,256,257,275,282,295,318,323,339,351,373,384,402,415,423,442,455],{"__ignoreMap":254},[258,259,261,265,269,272],"span",{"class":108,"line":260},1,[258,262,264],{"class":263},"snl16","from",[258,266,268],{"class":267},"s95oV"," qgis.core ",[258,270,271],{"class":263},"import",[258,273,274],{"class":267}," QgsVectorLayer, QgsRasterLayer, QgsApplication\n",[258,276,278],{"class":108,"line":277},2,[258,279,281],{"emptyLinePlaceholder":280},true,"\n",[258,283,285,288,291],{"class":108,"line":284},3,[258,286,287],{"class":267},"source ",[258,289,290],{"class":263},"=",[258,292,294],{"class":293},"sU2Wk"," \"\u002Fdata\u002Fwork\u002Fsurvey_2026.gpkg|layername=trees\"\n",[258,296,298,301,303,306,309,312,315],{"class":108,"line":297},4,[258,299,300],{"class":267},"layer ",[258,302,290],{"class":263},[258,304,305],{"class":267}," QgsVectorLayer(source, ",[258,307,308],{"class":293},"\"trees\"",[258,310,311],{"class":267},", ",[258,313,314],{"class":293},"\"ogr\"",[258,316,317],{"class":267},")\n",[258,319,321],{"class":108,"line":320},5,[258,322,281],{"emptyLinePlaceholder":280},[258,324,326,330,333,336],{"class":108,"line":325},6,[258,327,329],{"class":328},"sDLfK","print",[258,331,332],{"class":267},"(",[258,334,335],{"class":293},"\"valid:\"",[258,337,338],{"class":267},", layer.isValid())\n",[258,340,342,345,348],{"class":108,"line":341},7,[258,343,344],{"class":263},"if",[258,346,347],{"class":263}," not",[258,349,350],{"class":267}," layer.isValid():\n",[258,352,354,357,359,362,365,368,371],{"class":108,"line":353},8,[258,355,356],{"class":328},"    print",[258,358,332],{"class":267},[258,360,361],{"class":293},"\"layer error:   \"",[258,363,364],{"class":267},", layer.error().summary() ",[258,366,367],{"class":263},"or",[258,369,370],{"class":293}," \"(empty)\"",[258,372,317],{"class":267},[258,374,376,379,381],{"class":108,"line":375},9,[258,377,378],{"class":267},"    provider ",[258,380,290],{"class":263},[258,382,383],{"class":267}," layer.dataProvider()\n",[258,385,387,390,393,396,399],{"class":108,"line":386},10,[258,388,389],{"class":263},"    if",[258,391,392],{"class":267}," provider ",[258,394,395],{"class":263},"is",[258,397,398],{"class":328}," None",[258,400,401],{"class":267},":\n",[258,403,405,408,410,413],{"class":108,"line":404},11,[258,406,407],{"class":328},"        print",[258,409,332],{"class":267},[258,411,412],{"class":293},"\"no provider was created — check the provider key\"",[258,414,317],{"class":267},[258,416,418,421],{"class":108,"line":417},12,[258,419,420],{"class":263},"    else",[258,422,401],{"class":267},[258,424,426,428,430,433,436,438,440],{"class":108,"line":425},13,[258,427,407],{"class":328},[258,429,332],{"class":267},[258,431,432],{"class":293},"\"provider error:\"",[258,434,435],{"class":267},", provider.error().message() ",[258,437,367],{"class":263},[258,439,370],{"class":293},[258,441,317],{"class":267},[258,443,445,447,449,452],{"class":108,"line":444},14,[258,446,407],{"class":328},[258,448,332],{"class":267},[258,450,451],{"class":293},"\"provider valid:\"",[258,453,454],{"class":267},", provider.isValid())\n",[258,456,458,460,462,465],{"class":108,"line":457},15,[258,459,356],{"class":328},[258,461,332],{"class":267},[258,463,464],{"class":293},"\"prefix path:   \"",[258,466,467],{"class":267},", QgsApplication.prefixPath())\n",[14,469,470,473,474,477,478,481,482,484],{},[217,471,472],{},"Breakdown:"," ",[17,475,476],{},"error().summary()"," gives a one-line reason when the layer recorded one; ",[17,479,480],{},"message()"," on the provider error sometimes adds the underlying library's text. A ",[17,483,31],{}," data provider means QGIS could not create a provider at all — nearly always a misspelt or unavailable provider key — whereas a provider that exists but is invalid means the provider tried and failed to open the source. Printing the prefix path catches the standalone-script case where QGIS cannot find its own plugins and providers.",[206,486,488],{"id":487},"check-the-provider-key-and-the-path","Check the provider key and the path",[14,490,491],{},"Most invalid layers come from the two arguments people type by hand. Both can be checked in a few lines.",[14,493,494],{},[45,495,498,501,504,506,510,517,522,527,531,535,540,542,546,549,552,555,558,561,565,568,571,574,577,581,585,588,591,594],{"viewBox":496,"role":48,"ariaLabel":497,"xmlns":50},"0 0 760 256","Common source string mistakes and their fixes: a relative path that depends on the working directory, a wrong layername in a GeoPackage, a Windows path with unescaped backslashes, and the raster provider key used for a vector file",[52,499,500],{},"Four mistakes that make a layer invalid",[56,502,503],{},"Four cards. Relative path data\u002Froads.shp works in the console and fails in cron because the working directory differs; use an absolute path built from the script location. GeoPackage layername=Trees fails when the table is trees; list sublayers to get exact names. A Windows path C:\\new\\trees.shp in a normal Python string turns backslash n into a newline; use a raw string or forward slashes. A shapefile loaded with the gdal provider key is invalid because gdal reads rasters; use ogr.",[60,505],{"x":62,"y":62,"width":63,"height":146,"fill":65},[84,507,509],{"x":86,"y":508,"style":88,"fill":89,"textAnchor":90},"26","The source string is usually the culprit",[60,511],{"x":512,"y":513,"width":514,"height":515,"rx":163,"fill":164,"stroke":516,"style":100},"24","44","170","192","#b91c1c",[84,518,521],{"x":519,"y":520,"style":170,"fill":516,"textAnchor":90},"109","68","relative path",[84,523,526],{"x":519,"y":524,"style":525,"fill":82,"textAnchor":90},"98","text-anchor:middle;font-size:9.5px;font-family:monospace","data\u002Froads.shp",[84,528,530],{"x":519,"y":529,"style":203,"fill":82,"textAnchor":90},"140","works in the console,",[84,532,534],{"x":519,"y":533,"style":203,"fill":82,"textAnchor":90},"158","fails under cron",[84,536,539],{"x":519,"y":537,"style":203,"fill":538,"textAnchor":90},"206","#15803d","build absolute paths",[60,541],{"x":190,"y":513,"width":514,"height":515,"rx":163,"fill":164,"stroke":516,"style":100},[84,543,545],{"x":544,"y":520,"style":170,"fill":516,"textAnchor":90},"289","wrong layername",[84,547,548],{"x":544,"y":524,"style":525,"fill":82,"textAnchor":90},"…gpkg|layername=Trees",[84,550,551],{"x":544,"y":529,"style":203,"fill":82,"textAnchor":90},"table is actually",[84,553,554],{"x":544,"y":533,"style":203,"fill":82,"textAnchor":90},"called trees",[84,556,557],{"x":544,"y":537,"style":203,"fill":538,"textAnchor":90},"list sublayers first",[60,559],{"x":560,"y":513,"width":514,"height":515,"rx":163,"fill":164,"stroke":516,"style":100},"384",[84,562,564],{"x":563,"y":520,"style":170,"fill":516,"textAnchor":90},"469","backslashes",[84,566,567],{"x":563,"y":524,"style":525,"fill":82,"textAnchor":90},"\"C:\\new\\trees.shp\"",[84,569,570],{"x":563,"y":529,"style":203,"fill":82,"textAnchor":90},"\\n becomes a newline",[84,572,573],{"x":563,"y":533,"style":203,"fill":82,"textAnchor":90},"in the string",[84,575,576],{"x":563,"y":537,"style":203,"fill":538,"textAnchor":90},"raw strings or pathlib",[60,578],{"x":579,"y":513,"width":580,"height":515,"rx":163,"fill":164,"stroke":516,"style":100},"564","172",[84,582,584],{"x":583,"y":520,"style":170,"fill":516,"textAnchor":90},"650","wrong provider",[84,586,587],{"x":583,"y":524,"style":525,"fill":82,"textAnchor":90},"roads.shp, \"gdal\"",[84,589,590],{"x":583,"y":529,"style":203,"fill":82,"textAnchor":90},"gdal reads rasters,",[84,592,593],{"x":583,"y":533,"style":203,"fill":82,"textAnchor":90},"ogr reads vectors",[84,595,596],{"x":583,"y":537,"style":203,"fill":538,"textAnchor":90},"match layer class",[249,598,600],{"className":251,"code":599,"language":253,"meta":254,"style":254},"import os\nfrom pathlib import Path\nfrom qgis.core import QgsProviderRegistry\n\nregistry = QgsProviderRegistry.instance()\nkey = \"ogr\"\nprint(\"provider available:\", key in registry.providerList())\n\nparts = registry.decodeUri(key, source)\npath = parts.get(\"path\")\nprint(\"decoded:\", parts)\nif path:\n    p = Path(path)\n    print(\"absolute:\", p.is_absolute(), \"| exists:\", p.exists(),\n          \"| readable:\", os.access(p, os.R_OK), \"| size:\", p.stat().st_size if p.exists() else None)\n    print(\"cwd:\", Path.cwd())\n\nif path and path.lower().endswith((\".gpkg\", \".gdb\", \".sqlite\")):\n    names = [d.name() for d in registry.querySublayers(path)]\n    print(\"tables in container:\", names)\n    if parts.get(\"layerName\") and parts[\"layerName\"] not in names:\n        print(f\"no table called {parts['layerName']!r} — names are case-sensitive\")\n",[17,601,602,609,621,632,636,646,656,674,678,688,703,715,722,732,750,782,795,800,830,852,865,897],{"__ignoreMap":254},[258,603,604,606],{"class":108,"line":260},[258,605,271],{"class":263},[258,607,608],{"class":267}," os\n",[258,610,611,613,616,618],{"class":108,"line":277},[258,612,264],{"class":263},[258,614,615],{"class":267}," pathlib ",[258,617,271],{"class":263},[258,619,620],{"class":267}," Path\n",[258,622,623,625,627,629],{"class":108,"line":284},[258,624,264],{"class":263},[258,626,268],{"class":267},[258,628,271],{"class":263},[258,630,631],{"class":267}," QgsProviderRegistry\n",[258,633,634],{"class":108,"line":297},[258,635,281],{"emptyLinePlaceholder":280},[258,637,638,641,643],{"class":108,"line":320},[258,639,640],{"class":267},"registry ",[258,642,290],{"class":263},[258,644,645],{"class":267}," QgsProviderRegistry.instance()\n",[258,647,648,651,653],{"class":108,"line":325},[258,649,650],{"class":267},"key ",[258,652,290],{"class":263},[258,654,655],{"class":293}," \"ogr\"\n",[258,657,658,660,662,665,668,671],{"class":108,"line":341},[258,659,329],{"class":328},[258,661,332],{"class":267},[258,663,664],{"class":293},"\"provider available:\"",[258,666,667],{"class":267},", key ",[258,669,670],{"class":263},"in",[258,672,673],{"class":267}," registry.providerList())\n",[258,675,676],{"class":108,"line":353},[258,677,281],{"emptyLinePlaceholder":280},[258,679,680,683,685],{"class":108,"line":375},[258,681,682],{"class":267},"parts ",[258,684,290],{"class":263},[258,686,687],{"class":267}," registry.decodeUri(key, source)\n",[258,689,690,693,695,698,701],{"class":108,"line":386},[258,691,692],{"class":267},"path ",[258,694,290],{"class":263},[258,696,697],{"class":267}," parts.get(",[258,699,700],{"class":293},"\"path\"",[258,702,317],{"class":267},[258,704,705,707,709,712],{"class":108,"line":404},[258,706,329],{"class":328},[258,708,332],{"class":267},[258,710,711],{"class":293},"\"decoded:\"",[258,713,714],{"class":267},", parts)\n",[258,716,717,719],{"class":108,"line":417},[258,718,344],{"class":263},[258,720,721],{"class":267}," path:\n",[258,723,724,727,729],{"class":108,"line":425},[258,725,726],{"class":267},"    p ",[258,728,290],{"class":263},[258,730,731],{"class":267}," Path(path)\n",[258,733,734,736,738,741,744,747],{"class":108,"line":444},[258,735,356],{"class":328},[258,737,332],{"class":267},[258,739,740],{"class":293},"\"absolute:\"",[258,742,743],{"class":267},", p.is_absolute(), ",[258,745,746],{"class":293},"\"| exists:\"",[258,748,749],{"class":267},", p.exists(),\n",[258,751,752,755,758,761,764,767,770,772,775,778,780],{"class":108,"line":457},[258,753,754],{"class":293},"          \"| readable:\"",[258,756,757],{"class":267},", os.access(p, os.",[258,759,760],{"class":328},"R_OK",[258,762,763],{"class":267},"), ",[258,765,766],{"class":293},"\"| size:\"",[258,768,769],{"class":267},", p.stat().st_size ",[258,771,344],{"class":263},[258,773,774],{"class":267}," p.exists() ",[258,776,777],{"class":263},"else",[258,779,398],{"class":328},[258,781,317],{"class":267},[258,783,785,787,789,792],{"class":108,"line":784},16,[258,786,356],{"class":328},[258,788,332],{"class":267},[258,790,791],{"class":293},"\"cwd:\"",[258,793,794],{"class":267},", Path.cwd())\n",[258,796,798],{"class":108,"line":797},17,[258,799,281],{"emptyLinePlaceholder":280},[258,801,803,805,808,811,814,817,819,822,824,827],{"class":108,"line":802},18,[258,804,344],{"class":263},[258,806,807],{"class":267}," path ",[258,809,810],{"class":263},"and",[258,812,813],{"class":267}," path.lower().endswith((",[258,815,816],{"class":293},"\".gpkg\"",[258,818,311],{"class":267},[258,820,821],{"class":293},"\".gdb\"",[258,823,311],{"class":267},[258,825,826],{"class":293},"\".sqlite\"",[258,828,829],{"class":267},")):\n",[258,831,833,836,838,841,844,847,849],{"class":108,"line":832},19,[258,834,835],{"class":267},"    names ",[258,837,290],{"class":263},[258,839,840],{"class":267}," [d.name() ",[258,842,843],{"class":263},"for",[258,845,846],{"class":267}," d ",[258,848,670],{"class":263},[258,850,851],{"class":267}," registry.querySublayers(path)]\n",[258,853,855,857,859,862],{"class":108,"line":854},20,[258,856,356],{"class":328},[258,858,332],{"class":267},[258,860,861],{"class":293},"\"tables in container:\"",[258,863,864],{"class":267},", names)\n",[258,866,868,870,872,875,878,880,883,885,888,891,894],{"class":108,"line":867},21,[258,869,389],{"class":263},[258,871,697],{"class":267},[258,873,874],{"class":293},"\"layerName\"",[258,876,877],{"class":267},") ",[258,879,810],{"class":263},[258,881,882],{"class":267}," parts[",[258,884,874],{"class":293},[258,886,887],{"class":267},"] ",[258,889,890],{"class":263},"not",[258,892,893],{"class":263}," in",[258,895,896],{"class":267}," names:\n",[258,898,900,902,904,907,910,913,916,919,922,925,928,931],{"class":108,"line":899},22,[258,901,407],{"class":328},[258,903,332],{"class":267},[258,905,906],{"class":263},"f",[258,908,909],{"class":293},"\"no table called ",[258,911,912],{"class":328},"{",[258,914,915],{"class":267},"parts[",[258,917,918],{"class":293},"'layerName'",[258,920,921],{"class":267},"]",[258,923,924],{"class":263},"!r",[258,926,927],{"class":328},"}",[258,929,930],{"class":293}," — names are case-sensitive\"",[258,932,317],{"class":267},[14,934,935,473,937,940,941,944,945,948,949,952,953,957],{},[217,936,472],{},[17,938,939],{},"providerList()"," is the authoritative list of provider keys in this installation; a minimal Docker image may lack providers such as ",[17,942,943],{},"wms"," or ",[17,946,947],{},"mssql"," that a desktop install has. Decoding the source shows how QGIS actually parsed it, which exposes a stray ",[17,950,951],{},"|"," or a mangled Windows path immediately. A relative path resolves against the current working directory, which is the script's folder in one context and the home directory in another, so relative paths are the classic cause of \"works interactively, fails when scheduled\". A zero-byte file or a file that is not readable points at a copy that did not finish or a permissions problem. For containers, listing the real table names — as in ",[37,954,956],{"href":955},"\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Flist-and-load-geopackage-sublayers-pyqgis\u002F","listing and loading GeoPackage sublayers"," — settles layer-name mistakes, including case.",[206,959,961],{"id":960},"ask-gdal-directly","Ask GDAL directly",[14,963,964],{},"When QGIS's own errors are empty and the path is fine, open the same source with GDAL or OGR. The driver reports its own error text, which is usually specific: an unsupported format version, a corrupt header, a missing projection file, a locked SQLite database.",[249,966,968],{"className":251,"code":967,"language":253,"meta":254,"style":254},"from osgeo import gdal, ogr\n\ngdal.UseExceptions()\nogr.UseExceptions()\n\ndef probe(path, raster=False):\n    try:\n        if raster:\n            ds = gdal.Open(path)\n            print(\"GDAL driver:\", ds.GetDriver().ShortName, ds.RasterXSize, \"x\", ds.RasterYSize)\n        else:\n            ds = ogr.Open(path)\n            print(\"OGR driver:\", ds.GetDriver().GetName(),\n                  \"| layers:\", [ds.GetLayer(i).GetName() for i in range(ds.GetLayerCount())])\n    except RuntimeError as exc:\n        print(\"driver says:\", exc)\n\nprobe(\"\u002Fdata\u002Fwork\u002Fsurvey_2026.gpkg\")\nprobe(\"\u002Fdata\u002Fimagery\u002Fortho_2025.jp2\", raster=True)\nprint(\"JP2 drivers:\", [d for d in (\"JP2OpenJPEG\", \"JP2ECW\", \"JP2KAK\")\n                       if gdal.GetDriverByName(d) is not None])\n",[17,969,970,982,986,991,996,1000,1019,1026,1034,1044,1063,1070,1079,1091,1112,1126,1138,1142,1152,1172,1208],{"__ignoreMap":254},[258,971,972,974,977,979],{"class":108,"line":260},[258,973,264],{"class":263},[258,975,976],{"class":267}," osgeo ",[258,978,271],{"class":263},[258,980,981],{"class":267}," gdal, ogr\n",[258,983,984],{"class":108,"line":277},[258,985,281],{"emptyLinePlaceholder":280},[258,987,988],{"class":108,"line":284},[258,989,990],{"class":267},"gdal.UseExceptions()\n",[258,992,993],{"class":108,"line":297},[258,994,995],{"class":267},"ogr.UseExceptions()\n",[258,997,998],{"class":108,"line":320},[258,999,281],{"emptyLinePlaceholder":280},[258,1001,1002,1005,1009,1012,1014,1016],{"class":108,"line":325},[258,1003,1004],{"class":263},"def",[258,1006,1008],{"class":1007},"svObZ"," probe",[258,1010,1011],{"class":267},"(path, raster",[258,1013,290],{"class":263},[258,1015,27],{"class":328},[258,1017,1018],{"class":267},"):\n",[258,1020,1021,1024],{"class":108,"line":341},[258,1022,1023],{"class":263},"    try",[258,1025,401],{"class":267},[258,1027,1028,1031],{"class":108,"line":353},[258,1029,1030],{"class":263},"        if",[258,1032,1033],{"class":267}," raster:\n",[258,1035,1036,1039,1041],{"class":108,"line":375},[258,1037,1038],{"class":267},"            ds ",[258,1040,290],{"class":263},[258,1042,1043],{"class":267}," gdal.Open(path)\n",[258,1045,1046,1049,1051,1054,1057,1060],{"class":108,"line":386},[258,1047,1048],{"class":328},"            print",[258,1050,332],{"class":267},[258,1052,1053],{"class":293},"\"GDAL driver:\"",[258,1055,1056],{"class":267},", ds.GetDriver().ShortName, ds.RasterXSize, ",[258,1058,1059],{"class":293},"\"x\"",[258,1061,1062],{"class":267},", ds.RasterYSize)\n",[258,1064,1065,1068],{"class":108,"line":404},[258,1066,1067],{"class":263},"        else",[258,1069,401],{"class":267},[258,1071,1072,1074,1076],{"class":108,"line":417},[258,1073,1038],{"class":267},[258,1075,290],{"class":263},[258,1077,1078],{"class":267}," ogr.Open(path)\n",[258,1080,1081,1083,1085,1088],{"class":108,"line":425},[258,1082,1048],{"class":328},[258,1084,332],{"class":267},[258,1086,1087],{"class":293},"\"OGR driver:\"",[258,1089,1090],{"class":267},", ds.GetDriver().GetName(),\n",[258,1092,1093,1096,1099,1101,1104,1106,1109],{"class":108,"line":444},[258,1094,1095],{"class":293},"                  \"| layers:\"",[258,1097,1098],{"class":267},", [ds.GetLayer(i).GetName() ",[258,1100,843],{"class":263},[258,1102,1103],{"class":267}," i ",[258,1105,670],{"class":263},[258,1107,1108],{"class":328}," range",[258,1110,1111],{"class":267},"(ds.GetLayerCount())])\n",[258,1113,1114,1117,1120,1123],{"class":108,"line":457},[258,1115,1116],{"class":263},"    except",[258,1118,1119],{"class":328}," RuntimeError",[258,1121,1122],{"class":263}," as",[258,1124,1125],{"class":267}," exc:\n",[258,1127,1128,1130,1132,1135],{"class":108,"line":784},[258,1129,407],{"class":328},[258,1131,332],{"class":267},[258,1133,1134],{"class":293},"\"driver says:\"",[258,1136,1137],{"class":267},", exc)\n",[258,1139,1140],{"class":108,"line":797},[258,1141,281],{"emptyLinePlaceholder":280},[258,1143,1144,1147,1150],{"class":108,"line":802},[258,1145,1146],{"class":267},"probe(",[258,1148,1149],{"class":293},"\"\u002Fdata\u002Fwork\u002Fsurvey_2026.gpkg\"",[258,1151,317],{"class":267},[258,1153,1154,1156,1159,1161,1165,1167,1170],{"class":108,"line":832},[258,1155,1146],{"class":267},[258,1157,1158],{"class":293},"\"\u002Fdata\u002Fimagery\u002Fortho_2025.jp2\"",[258,1160,311],{"class":267},[258,1162,1164],{"class":1163},"s9osk","raster",[258,1166,290],{"class":263},[258,1168,1169],{"class":328},"True",[258,1171,317],{"class":267},[258,1173,1174,1176,1178,1181,1184,1186,1188,1190,1193,1196,1198,1201,1203,1206],{"class":108,"line":854},[258,1175,329],{"class":328},[258,1177,332],{"class":267},[258,1179,1180],{"class":293},"\"JP2 drivers:\"",[258,1182,1183],{"class":267},", [d ",[258,1185,843],{"class":263},[258,1187,846],{"class":267},[258,1189,670],{"class":263},[258,1191,1192],{"class":267}," (",[258,1194,1195],{"class":293},"\"JP2OpenJPEG\"",[258,1197,311],{"class":267},[258,1199,1200],{"class":293},"\"JP2ECW\"",[258,1202,311],{"class":267},[258,1204,1205],{"class":293},"\"JP2KAK\"",[258,1207,317],{"class":267},[258,1209,1210,1213,1216,1218,1220,1222],{"class":108,"line":867},[258,1211,1212],{"class":263},"                       if",[258,1214,1215],{"class":267}," gdal.GetDriverByName(d) ",[258,1217,395],{"class":263},[258,1219,347],{"class":263},[258,1221,398],{"class":328},[258,1223,1224],{"class":267},"])\n",[14,1226,1227,473,1229,1232,1233,1235,1236,1239,1240,1243],{},[217,1228,472],{},[17,1230,1231],{},"UseExceptions"," makes GDAL raise instead of returning ",[17,1234,31],{},", so the driver's message arrives as the exception text. GDAL is the library underneath the ",[17,1237,1238],{},"ogr"," and ",[17,1241,1242],{},"gdal"," providers, so if it cannot open the file, QGIS cannot either, and its message is the real reason. Checking which drivers exist is how you discover that a format depends on an optional driver — JPEG 2000, MrSID, some File Geodatabase features — that your installation was built without. For locked databases, the message names the lock; close the other program or copy the file.",[14,1245,1246],{},[45,1247,1250,1253,1256,1258,1261,1266,1269,1272,1276,1279,1282,1286],{"viewBox":1248,"role":48,"ariaLabel":1249,"xmlns":50},"0 0 760 236","Diagnosing remote layers: separate DNS and network reachability, TLS and proxy, authentication, and permission on the specific table or layer, each tested on its own so the failing layer of the stack is obvious",[52,1251,1252],{},"Peel remote failures layer by layer",[56,1254,1255],{},"Four stacked checks for a PostGIS or web layer. Network: can the host be resolved and reached on its port. Transport: does TLS succeed and does the proxy allow it. Authentication: does the auth configuration exist and do the credentials still work. Permission: can this user read this schema, table or service layer. Each check has a small test, and the first one that fails is where to look.",[60,1257],{"x":62,"y":62,"width":63,"height":161,"fill":65},[84,1259,1260],{"x":86,"y":508,"style":88,"fill":89,"textAnchor":90},"Remote layers fail at one of four levels",[60,1262],{"x":1263,"y":513,"width":1264,"height":1265,"rx":74,"fill":138,"stroke":139,"style":100},"120","520","38",[84,1267,1268],{"x":86,"y":520,"style":105,"fill":82,"textAnchor":90},"network — host resolves, port reachable",[60,1270],{"x":1263,"y":1271,"width":1264,"height":1265,"rx":74,"fill":98,"stroke":99,"style":100},"90",[84,1273,1275],{"x":86,"y":1274,"style":105,"fill":82,"textAnchor":90},"114","transport — TLS certificate trusted, proxy allows it",[60,1277],{"x":1263,"y":1278,"width":1264,"height":1265,"rx":74,"fill":152,"stroke":153,"style":100},"136",[84,1280,1281],{"x":86,"y":126,"style":105,"fill":82,"textAnchor":90},"authentication — auth config exists, credentials valid",[60,1283],{"x":1263,"y":1284,"width":1264,"height":1265,"rx":74,"fill":1285,"stroke":538,"style":100},"182","#e8efe6",[84,1287,1288],{"x":86,"y":537,"style":105,"fill":82,"textAnchor":90},"permission — this user may read this table or layer",[206,1290,1292],{"id":1291},"databases-and-web-services","Databases and web services",[14,1294,1295],{},"Remote layers can fail at the network, at TLS or a proxy, at authentication, or at permissions on one table. Testing each separately finds the level quickly.",[249,1297,1299],{"className":251,"code":1298,"language":253,"meta":254,"style":254},"import socket\nfrom qgis.core import QgsDataSourceUri, QgsApplication, QgsProviderRegistry\n\npg = QgsDataSourceUri(\"service='gis_prod' sslmode=require authcfg=pg00001 \"\n                      \"key='fid' table=\\\"cadastre\\\".\\\"parcels\\\" (geom)\")\n\nhost = pg.host() or \"db.internal\"\nport = int(pg.port() or 5432)\ntry:\n    socket.create_connection((host, port), timeout=5).close()\n    print(\"network: ok\")\nexcept OSError as exc:\n    print(\"network:\", exc)\n\nmanager = QgsApplication.authManager()\ncfg = pg.authConfigId()\nprint(\"auth config present:\", cfg in manager.configIds() if cfg else \"none used\")\n\nmd = QgsProviderRegistry.instance().providerMetadata(\"postgres\")\ntry:\n    conn = md.createConnection(pg.uri(False), {})\n    tables = [t.tableName() for t in conn.tables(\"cadastre\")]\n    print(\"can list schema; parcels visible:\", \"parcels\" in tables)\nexcept Exception as exc:\n    print(\"connection:\", exc)\n",[17,1300,1301,1308,1319,1323,1336,1363,1367,1382,1402,1409,1424,1435,1447,1458,1462,1472,1482,1511,1515,1530,1536,1551,1577,1597,1609],{"__ignoreMap":254},[258,1302,1303,1305],{"class":108,"line":260},[258,1304,271],{"class":263},[258,1306,1307],{"class":267}," socket\n",[258,1309,1310,1312,1314,1316],{"class":108,"line":277},[258,1311,264],{"class":263},[258,1313,268],{"class":267},[258,1315,271],{"class":263},[258,1317,1318],{"class":267}," QgsDataSourceUri, QgsApplication, QgsProviderRegistry\n",[258,1320,1321],{"class":108,"line":284},[258,1322,281],{"emptyLinePlaceholder":280},[258,1324,1325,1328,1330,1333],{"class":108,"line":297},[258,1326,1327],{"class":267},"pg ",[258,1329,290],{"class":263},[258,1331,1332],{"class":267}," QgsDataSourceUri(",[258,1334,1335],{"class":293},"\"service='gis_prod' sslmode=require authcfg=pg00001 \"\n",[258,1337,1338,1341,1344,1347,1349,1351,1353,1356,1358,1361],{"class":108,"line":320},[258,1339,1340],{"class":293},"                      \"key='fid' table=",[258,1342,1343],{"class":328},"\\\"",[258,1345,1346],{"class":293},"cadastre",[258,1348,1343],{"class":328},[258,1350,240],{"class":293},[258,1352,1343],{"class":328},[258,1354,1355],{"class":293},"parcels",[258,1357,1343],{"class":328},[258,1359,1360],{"class":293}," (geom)\"",[258,1362,317],{"class":267},[258,1364,1365],{"class":108,"line":325},[258,1366,281],{"emptyLinePlaceholder":280},[258,1368,1369,1372,1374,1377,1379],{"class":108,"line":341},[258,1370,1371],{"class":267},"host ",[258,1373,290],{"class":263},[258,1375,1376],{"class":267}," pg.host() ",[258,1378,367],{"class":263},[258,1380,1381],{"class":293}," \"db.internal\"\n",[258,1383,1384,1387,1389,1392,1395,1397,1400],{"class":108,"line":353},[258,1385,1386],{"class":267},"port ",[258,1388,290],{"class":263},[258,1390,1391],{"class":328}," int",[258,1393,1394],{"class":267},"(pg.port() ",[258,1396,367],{"class":263},[258,1398,1399],{"class":328}," 5432",[258,1401,317],{"class":267},[258,1403,1404,1407],{"class":108,"line":375},[258,1405,1406],{"class":263},"try",[258,1408,401],{"class":267},[258,1410,1411,1414,1417,1419,1421],{"class":108,"line":386},[258,1412,1413],{"class":267},"    socket.create_connection((host, port), ",[258,1415,1416],{"class":1163},"timeout",[258,1418,290],{"class":263},[258,1420,75],{"class":328},[258,1422,1423],{"class":267},").close()\n",[258,1425,1426,1428,1430,1433],{"class":108,"line":404},[258,1427,356],{"class":328},[258,1429,332],{"class":267},[258,1431,1432],{"class":293},"\"network: ok\"",[258,1434,317],{"class":267},[258,1436,1437,1440,1443,1445],{"class":108,"line":417},[258,1438,1439],{"class":263},"except",[258,1441,1442],{"class":328}," OSError",[258,1444,1122],{"class":263},[258,1446,1125],{"class":267},[258,1448,1449,1451,1453,1456],{"class":108,"line":425},[258,1450,356],{"class":328},[258,1452,332],{"class":267},[258,1454,1455],{"class":293},"\"network:\"",[258,1457,1137],{"class":267},[258,1459,1460],{"class":108,"line":444},[258,1461,281],{"emptyLinePlaceholder":280},[258,1463,1464,1467,1469],{"class":108,"line":457},[258,1465,1466],{"class":267},"manager ",[258,1468,290],{"class":263},[258,1470,1471],{"class":267}," QgsApplication.authManager()\n",[258,1473,1474,1477,1479],{"class":108,"line":784},[258,1475,1476],{"class":267},"cfg ",[258,1478,290],{"class":263},[258,1480,1481],{"class":267}," pg.authConfigId()\n",[258,1483,1484,1486,1488,1491,1494,1496,1499,1501,1504,1506,1509],{"class":108,"line":797},[258,1485,329],{"class":328},[258,1487,332],{"class":267},[258,1489,1490],{"class":293},"\"auth config present:\"",[258,1492,1493],{"class":267},", cfg ",[258,1495,670],{"class":263},[258,1497,1498],{"class":267}," manager.configIds() ",[258,1500,344],{"class":263},[258,1502,1503],{"class":267}," cfg ",[258,1505,777],{"class":263},[258,1507,1508],{"class":293}," \"none used\"",[258,1510,317],{"class":267},[258,1512,1513],{"class":108,"line":802},[258,1514,281],{"emptyLinePlaceholder":280},[258,1516,1517,1520,1522,1525,1528],{"class":108,"line":832},[258,1518,1519],{"class":267},"md ",[258,1521,290],{"class":263},[258,1523,1524],{"class":267}," QgsProviderRegistry.instance().providerMetadata(",[258,1526,1527],{"class":293},"\"postgres\"",[258,1529,317],{"class":267},[258,1531,1532,1534],{"class":108,"line":854},[258,1533,1406],{"class":263},[258,1535,401],{"class":267},[258,1537,1538,1541,1543,1546,1548],{"class":108,"line":867},[258,1539,1540],{"class":267},"    conn ",[258,1542,290],{"class":263},[258,1544,1545],{"class":267}," md.createConnection(pg.uri(",[258,1547,27],{"class":328},[258,1549,1550],{"class":267},"), {})\n",[258,1552,1553,1556,1558,1561,1563,1566,1568,1571,1574],{"class":108,"line":899},[258,1554,1555],{"class":267},"    tables ",[258,1557,290],{"class":263},[258,1559,1560],{"class":267}," [t.tableName() ",[258,1562,843],{"class":263},[258,1564,1565],{"class":267}," t ",[258,1567,670],{"class":263},[258,1569,1570],{"class":267}," conn.tables(",[258,1572,1573],{"class":293},"\"cadastre\"",[258,1575,1576],{"class":267},")]\n",[258,1578,1580,1582,1584,1587,1589,1592,1594],{"class":108,"line":1579},23,[258,1581,356],{"class":328},[258,1583,332],{"class":267},[258,1585,1586],{"class":293},"\"can list schema; parcels visible:\"",[258,1588,311],{"class":267},[258,1590,1591],{"class":293},"\"parcels\"",[258,1593,893],{"class":263},[258,1595,1596],{"class":267}," tables)\n",[258,1598,1600,1602,1605,1607],{"class":108,"line":1599},24,[258,1601,1439],{"class":263},[258,1603,1604],{"class":328}," Exception",[258,1606,1122],{"class":263},[258,1608,1125],{"class":267},[258,1610,1612,1614,1616,1619],{"class":108,"line":1611},25,[258,1613,356],{"class":328},[258,1615,332],{"class":267},[258,1617,1618],{"class":293},"\"connection:\"",[258,1620,1137],{"class":267},[14,1622,1623,1625,1626,1629,1630,1633,1634,1638],{},[217,1624,472],{}," A raw socket connection separates \"the server is unreachable\" from everything above it; when the URI uses a ",[17,1627,1628],{},"service="," entry, the host comes from ",[17,1631,1632],{},"pg_service.conf"," and must be looked up there instead. Checking that the authentication configuration exists catches projects copied to a machine where it was never created — the layer then tries to connect with no credentials. The connections API opens a real database session with the same URI and lists tables in the schema, so a table missing from the list is a permissions or naming problem rather than a connection problem. The same idea applies to web services: fetch the capabilities or landing page first, as in ",[37,1635,1637],{"href":1636},"\u002Fspatial-data-processing-automation\u002Fweb-services-and-remote-data\u002Fload-wfs-layer-pyqgis\u002F","loading a WFS layer",", and check the network logger in the developer tools panel.",[206,1640,1642],{"id":1641},"fail-loudly-with-a-loading-helper","Fail loudly with a loading helper",[14,1644,1645],{},"Once the checks are understood, fold them into one function that every script uses to load layers. An invalid layer becomes an exception with the source, the provider and every reason QGIS and GDAL gave.",[249,1647,1649],{"className":251,"code":1648,"language":253,"meta":254,"style":254},"from qgis.core import QgsMapLayer\n\nclass LayerLoadError(RuntimeError):\n    pass\n\ndef load_layer(source, name, provider=\"ogr\", kind=\"vector\"):\n    cls = QgsVectorLayer if kind == \"vector\" else QgsRasterLayer\n    layer = cls(source, name, provider)\n    if layer.isValid():\n        return layer\n\n    reasons = []\n    if provider not in QgsProviderRegistry.instance().providerList():\n        reasons.append(f\"provider {provider!r} not available\")\n    if layer.error().summary():\n        reasons.append(layer.error().summary())\n    dp = layer.dataProvider()\n    if dp is not None and dp.error().message():\n        reasons.append(dp.error().message())\n    path = QgsProviderRegistry.instance().decodeUri(provider, source).get(\"path\")\n    if path and not Path(path).exists():\n        reasons.append(f\"path does not exist: {path}\")\n    elif path:\n        try:\n            (gdal.Open if kind == \"raster\" else ogr.Open)(path)\n        except RuntimeError as exc:\n            reasons.append(f\"GDAL: {exc}\")\n\n    raise LayerLoadError(f\"could not load {name!r} from {source!r} ({provider}): \"\n                         + (\"; \".join(reasons) or \"no reason reported\"))\n\nroads = load_layer(\"\u002Fdata\u002Fwork\u002Fbase.gpkg|layername=roads\", \"roads\")\n",[17,1650,1651,1662,1666,1681,1686,1690,1714,1742,1755,1761,1769,1773,1783,1796,1820,1827,1832,1841,1860,1865,1879,1892,1912,1919,1926,1945,1957,1979,1984,2030,2052,2057],{"__ignoreMap":254},[258,1652,1653,1655,1657,1659],{"class":108,"line":260},[258,1654,264],{"class":263},[258,1656,268],{"class":267},[258,1658,271],{"class":263},[258,1660,1661],{"class":267}," QgsMapLayer\n",[258,1663,1664],{"class":108,"line":277},[258,1665,281],{"emptyLinePlaceholder":280},[258,1667,1668,1671,1674,1676,1679],{"class":108,"line":284},[258,1669,1670],{"class":263},"class",[258,1672,1673],{"class":1007}," LayerLoadError",[258,1675,332],{"class":267},[258,1677,1678],{"class":328},"RuntimeError",[258,1680,1018],{"class":267},[258,1682,1683],{"class":108,"line":297},[258,1684,1685],{"class":263},"    pass\n",[258,1687,1688],{"class":108,"line":320},[258,1689,281],{"emptyLinePlaceholder":280},[258,1691,1692,1694,1697,1700,1702,1704,1707,1709,1712],{"class":108,"line":325},[258,1693,1004],{"class":263},[258,1695,1696],{"class":1007}," load_layer",[258,1698,1699],{"class":267},"(source, name, provider",[258,1701,290],{"class":263},[258,1703,314],{"class":293},[258,1705,1706],{"class":267},", kind",[258,1708,290],{"class":263},[258,1710,1711],{"class":293},"\"vector\"",[258,1713,1018],{"class":267},[258,1715,1716,1719,1722,1725,1727,1730,1733,1736,1739],{"class":108,"line":341},[258,1717,1718],{"class":328},"    cls",[258,1720,1721],{"class":263}," =",[258,1723,1724],{"class":267}," QgsVectorLayer ",[258,1726,344],{"class":263},[258,1728,1729],{"class":267}," kind ",[258,1731,1732],{"class":263},"==",[258,1734,1735],{"class":293}," \"vector\"",[258,1737,1738],{"class":263}," else",[258,1740,1741],{"class":267}," QgsRasterLayer\n",[258,1743,1744,1747,1749,1752],{"class":108,"line":353},[258,1745,1746],{"class":267},"    layer ",[258,1748,290],{"class":263},[258,1750,1751],{"class":328}," cls",[258,1753,1754],{"class":267},"(source, name, provider)\n",[258,1756,1757,1759],{"class":108,"line":375},[258,1758,389],{"class":263},[258,1760,350],{"class":267},[258,1762,1763,1766],{"class":108,"line":386},[258,1764,1765],{"class":263},"        return",[258,1767,1768],{"class":267}," layer\n",[258,1770,1771],{"class":108,"line":404},[258,1772,281],{"emptyLinePlaceholder":280},[258,1774,1775,1778,1780],{"class":108,"line":417},[258,1776,1777],{"class":267},"    reasons ",[258,1779,290],{"class":263},[258,1781,1782],{"class":267}," []\n",[258,1784,1785,1787,1789,1791,1793],{"class":108,"line":425},[258,1786,389],{"class":263},[258,1788,392],{"class":267},[258,1790,890],{"class":263},[258,1792,893],{"class":263},[258,1794,1795],{"class":267}," QgsProviderRegistry.instance().providerList():\n",[258,1797,1798,1801,1803,1806,1808,1811,1813,1815,1818],{"class":108,"line":444},[258,1799,1800],{"class":267},"        reasons.append(",[258,1802,906],{"class":263},[258,1804,1805],{"class":293},"\"provider ",[258,1807,912],{"class":328},[258,1809,1810],{"class":267},"provider",[258,1812,924],{"class":263},[258,1814,927],{"class":328},[258,1816,1817],{"class":293}," not available\"",[258,1819,317],{"class":267},[258,1821,1822,1824],{"class":108,"line":457},[258,1823,389],{"class":263},[258,1825,1826],{"class":267}," layer.error().summary():\n",[258,1828,1829],{"class":108,"line":784},[258,1830,1831],{"class":267},"        reasons.append(layer.error().summary())\n",[258,1833,1834,1837,1839],{"class":108,"line":797},[258,1835,1836],{"class":267},"    dp ",[258,1838,290],{"class":263},[258,1840,383],{"class":267},[258,1842,1843,1845,1848,1850,1852,1854,1857],{"class":108,"line":802},[258,1844,389],{"class":263},[258,1846,1847],{"class":267}," dp ",[258,1849,395],{"class":263},[258,1851,347],{"class":263},[258,1853,398],{"class":328},[258,1855,1856],{"class":263}," and",[258,1858,1859],{"class":267}," dp.error().message():\n",[258,1861,1862],{"class":108,"line":832},[258,1863,1864],{"class":267},"        reasons.append(dp.error().message())\n",[258,1866,1867,1870,1872,1875,1877],{"class":108,"line":854},[258,1868,1869],{"class":267},"    path ",[258,1871,290],{"class":263},[258,1873,1874],{"class":267}," QgsProviderRegistry.instance().decodeUri(provider, source).get(",[258,1876,700],{"class":293},[258,1878,317],{"class":267},[258,1880,1881,1883,1885,1887,1889],{"class":108,"line":867},[258,1882,389],{"class":263},[258,1884,807],{"class":267},[258,1886,810],{"class":263},[258,1888,347],{"class":263},[258,1890,1891],{"class":267}," Path(path).exists():\n",[258,1893,1894,1896,1898,1901,1903,1905,1907,1910],{"class":108,"line":899},[258,1895,1800],{"class":267},[258,1897,906],{"class":263},[258,1899,1900],{"class":293},"\"path does not exist: ",[258,1902,912],{"class":328},[258,1904,79],{"class":267},[258,1906,927],{"class":328},[258,1908,1909],{"class":293},"\"",[258,1911,317],{"class":267},[258,1913,1914,1917],{"class":108,"line":1579},[258,1915,1916],{"class":263},"    elif",[258,1918,721],{"class":267},[258,1920,1921,1924],{"class":108,"line":1599},[258,1922,1923],{"class":263},"        try",[258,1925,401],{"class":267},[258,1927,1928,1931,1933,1935,1937,1940,1942],{"class":108,"line":1611},[258,1929,1930],{"class":267},"            (gdal.Open ",[258,1932,344],{"class":263},[258,1934,1729],{"class":267},[258,1936,1732],{"class":263},[258,1938,1939],{"class":293}," \"raster\"",[258,1941,1738],{"class":263},[258,1943,1944],{"class":267}," ogr.Open)(path)\n",[258,1946,1948,1951,1953,1955],{"class":108,"line":1947},26,[258,1949,1950],{"class":263},"        except",[258,1952,1119],{"class":328},[258,1954,1122],{"class":263},[258,1956,1125],{"class":267},[258,1958,1960,1963,1965,1968,1970,1973,1975,1977],{"class":108,"line":1959},27,[258,1961,1962],{"class":267},"            reasons.append(",[258,1964,906],{"class":263},[258,1966,1967],{"class":293},"\"GDAL: ",[258,1969,912],{"class":328},[258,1971,1972],{"class":267},"exc",[258,1974,927],{"class":328},[258,1976,1909],{"class":293},[258,1978,317],{"class":267},[258,1980,1982],{"class":108,"line":1981},28,[258,1983,281],{"emptyLinePlaceholder":280},[258,1985,1987,1990,1993,1995,1998,2000,2003,2005,2007,2010,2012,2015,2017,2019,2021,2023,2025,2027],{"class":108,"line":1986},29,[258,1988,1989],{"class":263},"    raise",[258,1991,1992],{"class":267}," LayerLoadError(",[258,1994,906],{"class":263},[258,1996,1997],{"class":293},"\"could not load ",[258,1999,912],{"class":328},[258,2001,2002],{"class":267},"name",[258,2004,924],{"class":263},[258,2006,927],{"class":328},[258,2008,2009],{"class":293}," from ",[258,2011,912],{"class":328},[258,2013,2014],{"class":267},"source",[258,2016,924],{"class":263},[258,2018,927],{"class":328},[258,2020,1192],{"class":293},[258,2022,912],{"class":328},[258,2024,1810],{"class":267},[258,2026,927],{"class":328},[258,2028,2029],{"class":293},"): \"\n",[258,2031,2033,2036,2038,2041,2044,2046,2049],{"class":108,"line":2032},30,[258,2034,2035],{"class":263},"                         +",[258,2037,1192],{"class":267},[258,2039,2040],{"class":293},"\"; \"",[258,2042,2043],{"class":267},".join(reasons) ",[258,2045,367],{"class":263},[258,2047,2048],{"class":293}," \"no reason reported\"",[258,2050,2051],{"class":267},"))\n",[258,2053,2055],{"class":108,"line":2054},31,[258,2056,281],{"emptyLinePlaceholder":280},[258,2058,2060,2063,2065,2068,2071,2073,2076],{"class":108,"line":2059},32,[258,2061,2062],{"class":267},"roads ",[258,2064,290],{"class":263},[258,2066,2067],{"class":267}," load_layer(",[258,2069,2070],{"class":293},"\"\u002Fdata\u002Fwork\u002Fbase.gpkg|layername=roads\"",[258,2072,311],{"class":267},[258,2074,2075],{"class":293},"\"roads\"",[258,2077,317],{"class":267},[14,2079,2080,2082,2083,2087],{},[217,2081,472],{}," The helper returns immediately in the normal case, so the diagnosis costs nothing when layers load. On failure it collects every source of explanation and raises once, with a message that is useful in a log file read the next morning — the discipline covered in ",[37,2084,2086],{"href":2085},"\u002Fpyqgis-fundamentals-environment-setup\u002Fheadless-qgis-and-server-automation\u002Fhandle-errors-and-logging-in-unattended-scripts\u002F","handling errors and logging in unattended scripts",". Raising a specific exception class lets callers catch layer failures separately from programming errors. Using the helper consistently removes a whole category of \"why is the extent empty\" debugging from every later script.",[206,2089,2091],{"id":2090},"qgis-version-compatibility","QGIS version compatibility",[14,2093,2094,311,2097,1239,2100,2103,2104,2107,2108,2111],{},[17,2095,2096],{},"isValid",[17,2098,2099],{},"error()",[17,2101,2102],{},"QgsProviderRegistry.decodeUri"," behave the same across QGIS 3.x and the QGIS 4 series. ",[17,2105,2106],{},"querySublayers"," needs 3.22 and the database connections API (",[17,2109,2110],{},"providerMetadata(...).createConnection",") 3.10, with table listing improved through later releases. GDAL's exception mode is recommended explicitly from GDAL 3.7 onwards, which every supported QGIS installer bundles.",[206,2113,2115],{"id":2114},"troubleshooting","Troubleshooting",[211,2117,2118,2126,2132,2141,2147],{},[214,2119,2120,473,2123,2125],{},[217,2121,2122],{},"Every layer is invalid, even known-good files.",[17,2124,229],{}," was not initialised or its prefix path is wrong.",[214,2127,2128,2131],{},[217,2129,2130],{},"Valid in the console, invalid in a scheduled job."," Relative paths or missing environment variables in the job's context.",[214,2133,2134,2137,2138,2140],{},[217,2135,2136],{},"Invalid only on one machine."," A missing driver, auth configuration or ",[17,2139,1632],{}," entry on that machine.",[214,2142,2143,2146],{},[217,2144,2145],{},"Raster loads but is blank."," Not an invalid layer — check the renderer and statistics instead.",[214,2148,2149,2152],{},[217,2150,2151],{},"Intermittently invalid on a network share."," A file still being written or locked; copy locally before loading.",[206,2154,2156],{"id":2155},"conclusion","Conclusion",[14,2158,2159,2160,2162],{},"Always check ",[17,2161,23],{},". When it is false, read the layer and provider errors, confirm the provider key and the decoded path, ask GDAL for the driver's own message, and test remote connections level by level. Then put those checks in a loading helper so every future invalid layer arrives as an exception that already explains itself.",[206,2164,2166],{"id":2165},"frequently-asked-questions","Frequently Asked Questions",[14,2168,2169,2172],{},[217,2170,2171],{},"Why does QGIS not raise an exception for an invalid layer?","\nBecause the C++ API reports validity through a flag, and the Python bindings preserve it. Projects routinely contain temporarily unavailable layers, which must load as placeholders rather than abort.",[14,2174,2175,2178,2179,2181],{},[217,2176,2177],{},"Can a layer become invalid after loading?","\nYes — a deleted file or dropped connection. Check ",[17,2180,23],{}," again before long operations on long-lived layers.",[14,2183,2184,2187,2188,1239,2191,2194],{},[217,2185,2186],{},"How do I see what QGIS Desktop does differently?","\nLoad the layer in Desktop, then compare ",[17,2189,2190],{},"layer.source()",[17,2192,2193],{},"layer.providerType()"," with the values in your script; the difference is usually visible at once.",[14,2196,2197,2204,2205,240],{},[217,2198,2199,2200,2203],{},"Is ",[17,2201,2202],{},"setDataSource"," a fix for an invalid layer?","\nIt is, once you know the correct source — see ",[37,2206,2208],{"href":2207},"\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Ffix-broken-layer-paths-in-project-pyqgis\u002F","fixing broken layer paths",[206,2210,2212],{"id":2211},"related","Related",[211,2214,2215,2220,2226,2232,2238],{},[214,2216,2217,2219],{},[37,2218,40],{"href":39}," — the guide this recipe belongs to",[214,2221,2222],{},[37,2223,2225],{"href":2224},"\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Fcatch-exceptions-and-tracebacks-pyqgis\u002F","Catch Exceptions and Read Tracebacks in PyQGIS",[214,2227,2228],{},[37,2229,2231],{"href":2230},"\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Ffixing-pyqgis-module-import-errors\u002F","Fixing PyQGIS Module Import Errors",[214,2233,2234],{},[37,2235,2237],{"href":2236},"\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Flog-messages-to-qgis-message-log-pyqgis\u002F","Log Messages to the QGIS Message Log in PyQGIS",[214,2239,2240],{},[37,2241,2243],{"href":2242},"\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Fdecode-and-build-data-source-uris-pyqgis\u002F","Decode and Build Data Source URIs in PyQGIS",[2245,2246,2247],"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 .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":254,"searchDepth":277,"depth":277,"links":2249},[2250,2251,2252,2253,2254,2255,2256,2257,2258,2259,2260],{"id":208,"depth":277,"text":209},{"id":243,"depth":277,"text":244},{"id":487,"depth":277,"text":488},{"id":960,"depth":277,"text":961},{"id":1291,"depth":277,"text":1292},{"id":1641,"depth":277,"text":1642},{"id":2090,"depth":277,"text":2091},{"id":2114,"depth":277,"text":2115},{"id":2155,"depth":277,"text":2156},{"id":2165,"depth":277,"text":2166},{"id":2211,"depth":277,"text":2212},"Find out why QgsVectorLayer or QgsRasterLayer returns an invalid layer — reading the layer and provider errors, checking the provider key and path, asking GDAL and OGR directly, diagnosing database and web connections, and wrapping layer loading in a helper that fails with a useful message.","md",{"slug":2264,"type":2265,"breadcrumb":2266,"datePublished":2267,"dateModified":2267},"debug-invalid-layer-loading-pyqgis","article","Debug Invalid Layer Loading","2026-09-17","\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Fdebug-invalid-layer-loading-pyqgis",{"title":5,"description":2261},"pyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Fdebug-invalid-layer-loading-pyqgis\u002Findex","no0MqQLIjITRz1ueH85fM-ODceEgP-IwyBSlSA_TvWw",1789632906587]