
Qgis Errors Data Loading
- 8 installs
- 29 repo stars
- Updated July 8, 2026
- openaec-foundation/qgis-claude-skill-package
Helps with ai & agent building tasks.
About
qgis-errors-data-loading is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- qgis-errors-data-loading
- AI & Agent Building
- AI-coding skill
Qgis Errors Data Loading by the numbers
- 8 all-time installs (skills.sh)
- Ranked #12,321 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/qgis-claude-skill-package --skill qgis-errors-data-loadingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 29 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/qgis-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
qgis-errors-data-loading
Quick Reference
The #1 Rule
ALWAYS call layer.isValid() immediately after creating ANY layer. An invalid layer does NOT raise an exception — it silently returns a broken object that crashes or produces undefined behavior on access.
layer = QgsVectorLayer(uri, name, provider)
if not layer.isValid():
raise RuntimeError(f"Failed to load layer '{name}': check URI, provider, and data source")Error Categories at a Glance
| Error Category | Symptoms | Typical Root Cause |
|---|---|---|
| Invalid layer | isValid() returns False | Wrong path, bad URI, missing provider |
| Wrong URI format | Layer loads with 0 features or fails silently | Provider-specific URI syntax violated |
| Encoding issues | Garbled attribute text, mojibake | Shapefile .dbf encoding mismatch |
| GeoPackage locking | "database is locked" error | Concurrent write access to .gpkg |
| PostGIS connection failure | Timeout, auth error, empty layer | Network, credentials, permissions |
| WMS/WFS errors | Blank tiles, timeout, parse failure | Service URL, capabilities, CRS mismatch |
| Large dataset performance | Memory exhaustion, UI freeze | Loading all features into memory at once |
| Missing CRS | Layer places at wrong location | No .prj file or CRS metadata absent |
| Shapefile limitations | Truncated field names, 2GB cap | Format constraints hit |
Critical Warnings
NEVER access features, attributes, or data provider methods on an invalid layer — this leads to crashes or undefined behavior.
NEVER use backslashes in file paths or URIs — QGIS/Qt normalizes to forward slashes internally. Backslashes in URIs cause provider failures.
NEVER pass True to uri.uri(expandAuthConfig) when logging or displaying URIs — this exposes credentials in plain text. ALWAYS use uri.uri(False).
NEVER hardcode absolute file paths in cross-platform code. ALWAYS use os.path.join() or pathlib.Path for path construction.
---
Error Catalog
E-001: layer.isValid() Returns False
Symptoms: Layer object exists but isValid() returns False. No exception raised. Adding to project shows a broken layer icon.
Root causes (check in order): 1. File does not exist at the specified path 2. URI format is wrong for the chosen provider 3. Provider name is misspelled or missing 4. Authentication credentials are missing or incorrect 5. CRS database is unavailable (standalone scripts without setPrefixPath) 6. Data file is corrupted or truncated
Fix pattern:
import os
from qgis.core import QgsVectorLayer
path = "/data/airports.shp"
# Step 1: Verify file exists
if not os.path.exists(path):
raise FileNotFoundError(f"Data file not found: {path}")
# Step 2: Load with explicit provider
layer = QgsVectorLayer(path, "Airports", "ogr")
# Step 3: ALWAYS check validity
if not layer.isValid():
# Step 4: Inspect error string for details
error = layer.dataProvider().error().message() if layer.dataProvider() else "No provider"
raise RuntimeError(f"Layer invalid: {error}")E-002: Wrong URI Format for Provider
Symptoms: Layer is invalid or loads with 0 features. No error message.
Correct URI formats by provider:
| Provider | Correct URI | Common Mistake |
|---|---|---|
ogr (Shapefile) | "/path/to/file.shp" | Missing file extension |
ogr (GeoPackage) | `"/path/to/file.gpkg\ | layername=roads"` |
ogr (GeoJSON) | "/path/to/file.geojson" | Using "geojson" as provider name |
postgres | Use QgsDataSourceUri object | Manual string concatenation |
wms | "crs=EPSG:4326&format=image/png&layers=name&styles&url=https://..." | Missing url= parameter |
WFS | "https://server/wfs?service=WFS&version=2.0.0&request=GetFeature&typename=ns:layer" | Wrong provider name (must be uppercase "WFS") |
delimitedtext | "file:///path/to/file.csv?delimiter=,&xField=lon&yField=lat" | Missing file:// prefix |
memory | "Point?crs=EPSG:4326&field=name:string(50)" | Missing geometry type |
spatialite | Use QgsDataSourceUri object | Raw path without URI builder |
gpx | "/path/to/file.gpx?type=track" | Missing ?type= parameter |
Fix pattern for GeoPackage:
# WRONG: loads first layer or fails silently
layer = QgsVectorLayer("/data/data.gpkg", "My Layer", "ogr")
# CORRECT: explicit layer name
layer = QgsVectorLayer("/data/data.gpkg|layername=roads", "Roads", "ogr")Fix pattern for PostGIS:
from qgis.core import QgsDataSourceUri, QgsVectorLayer
# ALWAYS use QgsDataSourceUri: NEVER concatenate strings
uri = QgsDataSourceUri()
uri.setConnection("localhost", "5432", "mydb", "user", "pass")
uri.setDataSource("public", "roads", "geom", "", "gid")
layer = QgsVectorLayer(uri.uri(False), "Roads", "postgres")E-003: Character Encoding Issues in Shapefile .dbf Files
Symptoms: Attribute values contain garbled characters (mojibake). Accented characters display incorrectly. Field values show é instead of é.
Root cause: Shapefile .dbf files use a code page byte in the header, but many tools write it incorrectly or omit it. QGIS/OGR falls back to system encoding, which may not match the actual encoding.
Fix pattern:
from qgis.core import QgsVectorLayer
# Method 1: Set encoding via open options in the URI
layer = QgsVectorLayer(
"/data/old_data.shp|option:ENCODING=UTF-8",
"Data", "ogr"
)
# Method 2: Create a .cpg file next to the .shp with the encoding name
# Write "UTF-8" or "ISO-8859-1" to /data/old_data.cpg
# Method 3: Set encoding after loading (for display only)
layer = QgsVectorLayer("/data/old_data.shp", "Data", "ogr")
if layer.isValid():
layer.dataProvider().setEncoding("UTF-8")Common encodings to try: UTF-8, ISO-8859-1 (Latin-1), Windows-1252, ISO-8859-15 (Latin-9 with Euro sign).
E-004: GeoPackage File Locking (Concurrent Access)
Symptoms: "database is locked" error. Write operations fail. Layer becomes read-only unexpectedly.
Root cause: GeoPackage uses SQLite, which has limited concurrent write support. Multiple processes or QGIS instances writing to the same .gpkg file cause locking conflicts. WAL (Write-Ahead Logging) mode can help but does not eliminate all issues.
Fix pattern:
# Prevention: Use journal_mode=WAL for better concurrent read support
# Set GDAL config option BEFORE loading
from osgeo import gdal
gdal.SetConfigOption("OGR_SQLITE_JOURNAL", "WAL")
# Detection: Check for lock files
import os
gpkg_path = "/data/project.gpkg"
lock_files = [
gpkg_path + "-wal",
gpkg_path + "-shm",
gpkg_path + "-journal"
]
for lock_file in lock_files:
if os.path.exists(lock_file):
print(f"Lock file present: {lock_file}")
# Resolution strategies:
# 1. Close all other QGIS instances accessing the file
# 2. Delete stale lock files (-wal, -shm, -journal) ONLY if no process is using the file
# 3. Copy the file, work on the copy, then replace the original
# 4. For multi-user workflows: use PostGIS instead of GeoPackageE-005: PostGIS Connection Failures
Symptoms: Layer is invalid. Error messages include "could not connect to server", "authentication failed", "permission denied", or "relation does not exist".
Diagnostic checklist: 1. Network: Can the machine reach the database host and port? 2. Authentication: Are username/password correct? Is the auth method configured in pg_hba.conf? 3. Database: Does the database exist? Does the user have CONNECT privilege? 4. Schema/Table: Does the table exist in the specified schema? Does the user have SELECT privilege? 5. Geometry column: Does the specified geometry column exist in the table? 6. Primary key: Is the specified key column a valid unique column?
Fix pattern:
from qgis.core import QgsDataSourceUri, QgsVectorLayer
uri = QgsDataSourceUri()
uri.setConnection("dbhost", "5432", "gisdb", "gisuser", "password")
uri.setDataSource("public", "parcels", "geom", "", "gid")
layer = QgsVectorLayer(uri.uri(False), "Parcels", "postgres")
if not layer.isValid():
# Check provider error for specific failure reason
provider = layer.dataProvider()
if provider:
print(f"Provider error: {provider.error().message()}")
else:
print("Provider could not be instantiated — check connection parameters")
# For production: ALWAYS use QgsAuthManager instead of plain passwords
uri_secure = QgsDataSourceUri()
uri_secure.setConnection("dbhost", "5432", "gisdb", "", "")
uri_secure.setAuthConfigId("my_authcfg_id") # Stored in encrypted qgis-auth.db
uri_secure.setDataSource("public", "parcels", "geom", "", "gid")NEVER store passwords in plain text URIs in production. ALWAYS use QgsAuthManager with stored authentication configurations.
NEVER load PostGIS views without specifying a unique key column — this causes undefined behavior and performance issues.
E-006: WMS/WFS Timeout or Capability Parsing Errors
Symptoms: WMS layer shows blank tiles or fails to load. WFS layer times out. GetCapabilities returns an XML parsing error.
Root causes: 1. Service URL is incorrect or unreachable 2. Layer name does not match the service capabilities 3. CRS is not supported by the service 4. Network timeout is too short for slow services 5. XYZ tile URL placeholders are not URL-encoded
Fix pattern for WMS:
from qgis.core import QgsRasterLayer
# CORRECT WMS URI format: note all required parameters
uri = (
"crs=EPSG:4326"
"&format=image/png"
"&layers=my_layer_name"
"&styles"
"&url=https://example.com/wms"
)
layer = QgsRasterLayer(uri, "WMS Layer", "wms")
if not layer.isValid():
print("WMS layer invalid — check URL, layer name, and CRS support")Fix pattern for XYZ tiles:
# ALWAYS URL-encode {z}, {x}, {y} placeholders
url = "type=xyz&url=https://tiles.example.com/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0"
layer = QgsRasterLayer(url, "Tiles", "wms")NEVER assume a WMS layer supports all CRS — check the GetCapabilities response first.
E-007: Large Dataset Performance Issues
Symptoms: QGIS freezes or runs out of memory. Feature iteration takes excessively long. Script hangs on getFeatures().
Root cause: Loading all features into memory at once for large datasets (>1M features) or iterating without spatial/attribute filters.
Fix pattern:
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsRectangle
layer = QgsVectorLayer("/data/huge_dataset.gpkg|layername=parcels", "Parcels", "ogr")
# WRONG: loads ALL features into memory
all_features = list(layer.getFeatures()) # Memory explosion for large datasets
# CORRECT: use spatial filter to limit features
bbox = QgsRectangle(100000, 400000, 110000, 410000)
request = QgsFeatureRequest().setFilterRect(bbox)
for feature in layer.getFeatures(request):
# Process feature
pass
# CORRECT: use attribute filter
request = QgsFeatureRequest().setFilterExpression('"status" = \'active\'')
for feature in layer.getFeatures(request):
pass
# CORRECT: limit returned attributes for performance
request = QgsFeatureRequest()
request.setSubsetOfAttributes(["name", "area"], layer.fields())
request.setFlags(QgsFeatureRequest.NoGeometry) # Skip geometry if not needed
for feature in layer.getFeatures(request):
pass
# CORRECT: use setLimit() to cap result count
request = QgsFeatureRequest().setLimit(1000)
for feature in layer.getFeatures(request):
passE-008: Missing CRS in Loaded Data
Symptoms: Layer loads but displays at wrong location. Features cluster at origin (0,0). Layer does not align with other layers.
Root cause: Data file has no CRS metadata (missing .prj file for Shapefile, no CRS in GeoJSON, etc.). QGIS assigns a default or no CRS.
Fix pattern:
from qgis.core import QgsVectorLayer, QgsCoordinateReferenceSystem
layer = QgsVectorLayer("/data/no_crs.shp", "Data", "ogr")
if layer.isValid():
# Check if CRS is valid
if not layer.crs().isValid():
print(f"WARNING: Layer has no valid CRS")
# Assign the correct CRS (does NOT reproject — just sets metadata)
layer.setCrs(QgsCoordinateReferenceSystem("EPSG:28992"))
# Verify CRS is what you expect
print(f"Layer CRS: {layer.crs().authid()}")NEVER confuse setCrs() (assigning metadata) with reprojection (coordinate transformation). setCrs() changes the label, not the coordinates. To reproject, use Processing native:reprojectlayer or QgsCoordinateTransform.
E-009: Shapefile Limitations
Symptoms: Field names truncated to 10 characters. File size capped at 2GB. Date/time fields lose precision. No support for NULL vs empty string.
Root cause: Shapefile is a legacy format with hard limitations from dBASE III.
Known limitations:
| Limitation | Details |
|---|---|
| Field name length | Maximum 10 characters — names are silently truncated |
| File size | Maximum 2GB per component file (.shp, .dbf) |
| Geometry types | Single geometry type per file — no mixed geometries |
| Character encoding | Relies on .cpg file or code page byte — unreliable |
| No NULL support | Cannot distinguish NULL from empty string or zero |
| Date precision | Date only, no time or datetime support in .dbf |
| No nested fields | No support for JSON, arrays, or nested structures |
Fix pattern: Migrate to GeoPackage for any new work:
import processing
# Convert Shapefile to GeoPackage
processing.run("native:package", {
'LAYERS': [shapefile_layer],
'OUTPUT': '/data/output.gpkg',
'OVERWRITE': True
})---
Diagnostic Flowchart
Layer fails to load or behaves unexpectedly
│
├─ Step 1: Does the file/service exist?
│ ├─ NO → Fix the file path or service URL
│ └─ YES ↓
│
├─ Step 2: Is layer.isValid() True?
│ ├─ NO → Check layer.dataProvider().error().message()
│ │ ├─ "Could not connect" → E-005 (PostGIS) or E-006 (WMS/WFS)
│ │ ├─ "not recognized as a supported file format" → E-002 (URI format)
│ │ ├─ "database is locked" → E-004 (GeoPackage locking)
│ │ └─ No provider at all → Wrong provider name in constructor
│ └─ YES ↓
│
├─ Step 3: Does the layer have features?
│ ├─ featureCount() == 0 → Check URI (E-002), filter expression, or empty source
│ └─ featureCount() > 0 ↓
│
├─ Step 4: Are features at the correct location?
│ ├─ NO → Check CRS (E-008) or coordinate order
│ └─ YES ↓
│
├─ Step 5: Are attribute values correct?
│ ├─ Garbled text → E-003 (encoding)
│ ├─ Truncated names → E-009 (Shapefile limits)
│ └─ YES ↓
│
├─ Step 6: Is performance acceptable?
│ ├─ NO → E-007 (large dataset optimization)
│ └─ YES → Layer is working correctly---
Fix Patterns Summary
| Error | First Action | Fallback Action |
|---|---|---|
| E-001: Invalid layer | Verify file path exists, check provider name | Inspect dataProvider().error().message() |
| E-002: Wrong URI | Compare URI against format table above | Use QgsDataSourceUri for database providers |
| E-003: Encoding | Create .cpg file with correct encoding | Set encoding via dataProvider().setEncoding() |
| E-004: GPKG lock | Close other processes accessing the file | Delete stale lock files, switch to PostGIS |
| E-005: PostGIS | Verify host/port/db/user/password | Check pg_hba.conf, schema permissions |
| E-006: WMS/WFS | Verify service URL in browser | Check GetCapabilities for layer names and CRS |
| E-007: Performance | Add spatial filter to QgsFeatureRequest | Use NoGeometry flag, limit attributes |
| E-008: Missing CRS | Assign CRS with setCrs() | Create .prj file alongside Shapefile |
| E-009: Shapefile limits | Migrate to GeoPackage | Accept limitations for legacy workflows |
---
Reference Links
- references/methods.md — Key API methods for layer loading, validity checking, and error inspection
- references/examples.md — Working diagnostic code examples for each error type
- references/anti-patterns.md — What NEVER to do when loading data
Official Sources
- https://docs.qgis.org/latest/en/docs/pyqgis_developer_cookbook/loadlayer.html
- https://qgis.org/pyqgis/3.44/core/QgsVectorLayer.html
- https://qgis.org/pyqgis/3.44/core/QgsRasterLayer.html
- https://qgis.org/pyqgis/3.44/core/QgsDataSourceUri.html
- https://docs.qgis.org/latest/en/docs/pyqgis_developer_cookbook/cheat_sheet.html
qgis-errors-data-loading — Anti-Patterns
AP-001: Not Checking Layer Validity
NEVER skip the isValid() check after layer creation.
# WRONG — silent failure, layer may be invalid
layer = QgsVectorLayer("/data/roads.shp", "Roads", "ogr")
QgsProject.instance().addMapLayer(layer) # Adds a broken layer
for feat in layer.getFeatures(): # Crashes or returns nothing
process(feat)
# CORRECT — ALWAYS check validity before use
layer = QgsVectorLayer("/data/roads.shp", "Roads", "ogr")
if not layer.isValid():
raise RuntimeError(f"Layer failed to load: {layer.dataProvider().error().message()}")
QgsProject.instance().addMapLayer(layer)Why: QGIS does NOT raise exceptions for invalid layers. The constructor ALWAYS returns an object, even when loading fails completely. Accessing features or providers on an invalid layer causes crashes or undefined behavior.
---
AP-002: Using Backslashes in File Paths
NEVER use backslashes in paths passed to QGIS layer constructors.
# WRONG — backslashes cause provider failures
layer = QgsVectorLayer("C:\\data\\roads.shp", "Roads", "ogr")
# CORRECT — forward slashes work on all platforms
layer = QgsVectorLayer("C:/data/roads.shp", "Roads", "ogr")
# CORRECT — use os.path for cross-platform safety
import os
path = os.path.join("C:/data", "roads.shp")
layer = QgsVectorLayer(path, "Roads", "ogr")Why: QGIS/Qt normalizes paths to forward slashes internally. Backslashes in URI strings break provider parsing, especially for GeoPackage (|layername=) and delimited text (?delimiter=) URIs.
---
AP-003: Hardcoding Absolute Paths
NEVER hardcode absolute paths that are specific to one machine.
# WRONG — breaks on any other machine
layer = QgsVectorLayer("/home/john/projects/gis/data/roads.shp", "Roads", "ogr")
# CORRECT — use project-relative paths
import os
project_dir = QgsProject.instance().homePath()
path = os.path.join(project_dir, "data", "roads.shp")
layer = QgsVectorLayer(path, "Roads", "ogr")Why: Hardcoded paths make scripts non-portable. They fail when the project is moved, shared, or deployed to a different environment.
---
AP-004: Manual String Concatenation for Database URIs
NEVER build database connection strings by concatenating strings.
# WRONG — error-prone, credential exposure risk
uri = "dbname='gisdb' host=localhost port=5432 user='admin' password='secret' table=\"public\".\"roads\" (geom)"
layer = QgsVectorLayer(uri, "Roads", "postgres")
# CORRECT — use QgsDataSourceUri
from qgis.core import QgsDataSourceUri
uri = QgsDataSourceUri()
uri.setConnection("localhost", "5432", "gisdb", "admin", "secret")
uri.setDataSource("public", "roads", "geom", "", "gid")
layer = QgsVectorLayer(uri.uri(False), "Roads", "postgres")Why: Manual concatenation is error-prone with quoting and escaping. QgsDataSourceUri handles all escaping correctly and provides uri(False) to prevent credential leaking.
---
AP-005: Exposing Credentials in URIs
NEVER pass True to uri.uri() when logging or displaying connection strings.
# WRONG — credentials visible in logs
uri = QgsDataSourceUri()
uri.setConnection("localhost", "5432", "gisdb", "admin", "s3cret!")
uri.setDataSource("public", "roads", "geom")
print(f"Connecting to: {uri.uri(True)}") # Prints password in plain text
# CORRECT — suppress credential expansion
print(f"Connecting to: {uri.uri(False)}")
# BEST — use QgsAuthManager for credentials
uri.setConnection("localhost", "5432", "gisdb", "", "")
uri.setAuthConfigId("my_auth_config")Why: uri(True) expands stored authentication configurations, embedding passwords in the output string. This exposes credentials in log files, error messages, and debug output.
---
AP-006: Assuming GeoPackage Has One Layer
NEVER assume a GeoPackage file contains only one layer.
# WRONG — loads the first layer, which may not be what you want
layer = QgsVectorLayer("/data/project.gpkg", "Data", "ogr")
# CORRECT — ALWAYS specify the layer name explicitly
layer = QgsVectorLayer("/data/project.gpkg|layername=buildings", "Buildings", "ogr")Why: GeoPackage files commonly contain multiple layers. Without |layername=, the OGR provider loads the first layer by index, which may not be the intended layer. This causes silent logic errors.
---
AP-007: Loading All Features from Large Datasets
NEVER load all features into a list for large datasets.
# WRONG — loads millions of features into memory
all_features = list(layer.getFeatures())
for feat in all_features:
process(feat)
# CORRECT — iterate directly (streaming, low memory)
for feat in layer.getFeatures():
process(feat)
# CORRECT — use filters to reduce the working set
request = QgsFeatureRequest().setFilterRect(area_of_interest)
for feat in layer.getFeatures(request):
process(feat)Why: list(layer.getFeatures()) loads every feature object into memory simultaneously. For datasets with millions of features, this causes memory exhaustion. The iterator pattern processes one feature at a time.
---
AP-008: Confusing setCrs() with Reprojection
NEVER use setCrs() to reproject data — it only changes the CRS label.
from qgis.core import QgsCoordinateReferenceSystem
# WRONG — this does NOT reproject coordinates
layer.setCrs(QgsCoordinateReferenceSystem("EPSG:4326"))
# Coordinates are still in the original CRS, but QGIS thinks they are WGS84
# CORRECT — use Processing to reproject
import processing
result = processing.run("native:reprojectlayer", {
'INPUT': layer,
'TARGET_CRS': QgsCoordinateReferenceSystem("EPSG:4326"),
'OUTPUT': 'memory:'
})
reprojected = result['OUTPUT']Why: setCrs() is metadata-only — it tells QGIS what CRS the coordinates are in. It does NOT transform the coordinate values. Using it incorrectly causes features to display at the wrong location.
---
AP-009: Not Specifying Key Column for PostGIS Views
NEVER load a PostGIS view without specifying a unique key column.
# WRONG — no key column specified for a view
uri = QgsDataSourceUri()
uri.setConnection("localhost", "5432", "gisdb", "user", "pass")
uri.setDataSource("public", "my_view", "geom")
layer = QgsVectorLayer(uri.uri(False), "My View", "postgres")
# CORRECT — ALWAYS specify a key column for views
uri.setDataSource("public", "my_view", "geom", "", "unique_id")
layer = QgsVectorLayer(uri.uri(False), "My View", "postgres")Why: PostGIS views do not have primary keys. Without a specified key column, QGIS cannot reliably identify features, causing undefined behavior during selection, editing, and feature iteration.
---
AP-010: Ignoring XYZ Tile URL Encoding
NEVER use raw {z}, {x}, {y} placeholders in XYZ tile URIs.
# WRONG — raw curly braces break URI parsing
url = "type=xyz&url=https://tiles.example.com/{z}/{x}/{y}.png"
layer = QgsRasterLayer(url, "Tiles", "wms")
# CORRECT — URL-encode the placeholders
url = "type=xyz&url=https://tiles.example.com/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0"
layer = QgsRasterLayer(url, "Tiles", "wms")Why: The curly braces are interpreted as URI template syntax by the parser rather than being passed through as literal characters. URL-encoding (%7B / %7D) ensures they are treated as literal text.
---
AP-011: Not Calling updateFields() After Adding Attributes
NEVER skip updateFields() after adding attributes to a memory layer.
from qgis.core import QgsVectorLayer, QgsField
from qgis.PyQt.QtCore import QVariant
layer = QgsVectorLayer("Point?crs=EPSG:4326", "Points", "memory")
pr = layer.dataProvider()
pr.addAttributes([QgsField("name", QVariant.String)])
# WRONG — fields not synced, feature creation fails silently
feat = QgsFeature(layer.fields()) # layer.fields() is empty!
# CORRECT — sync the layer's field cache
layer.updateFields()
feat = QgsFeature(layer.fields()) # Now has "name" fieldWhy: dataProvider().addAttributes() modifies the provider's schema, but the layer object caches its own copy of fields. Without updateFields(), the layer's field list is stale.
---
AP-012: Not Calling updateExtents() After Adding Features
NEVER skip updateExtents() after adding features to a memory layer.
# WRONG — extent is (0,0,0,0), zoom-to-layer fails
pr.addFeatures([feat1, feat2, feat3])
QgsProject.instance().addMapLayer(layer)
# CORRECT — update extent after features are added
pr.addFeatures([feat1, feat2, feat3])
layer.updateExtents()
QgsProject.instance().addMapLayer(layer)Why: Memory layers do not automatically recalculate their spatial extent when features are added. Without updateExtents(), the layer reports an empty extent, causing "zoom to layer" and spatial index operations to fail.
qgis-errors-data-loading — Examples
Example 1: Complete Layer Loading with Full Error Handling
import os
from qgis.core import QgsVectorLayer, QgsProject
def load_vector_layer(path, name, provider="ogr"):
"""Load a vector layer with comprehensive error checking."""
# Step 1: Verify file exists (for file-based providers)
if provider in ("ogr", "gdal", "spatialite", "gpx"):
# Strip URI parameters for file existence check
file_path = path.split("|")[0].split("?")[0]
if not os.path.exists(file_path):
raise FileNotFoundError(f"Data file not found: {file_path}")
# Step 2: Create layer
layer = QgsVectorLayer(path, name, provider)
# Step 3: ALWAYS check validity
if not layer.isValid():
dp = layer.dataProvider()
if dp:
error_msg = dp.error().message()
else:
error_msg = "Provider could not be instantiated"
raise RuntimeError(f"Failed to load '{name}': {error_msg}")
# Step 4: Verify CRS
if not layer.crs().isValid():
print(f"WARNING: Layer '{name}' has no valid CRS — assign one before analysis")
# Step 5: Verify features exist
if layer.featureCount() == 0:
print(f"WARNING: Layer '{name}' loaded successfully but contains 0 features")
return layer
# Usage
layer = load_vector_layer("/data/roads.gpkg|layername=roads", "Roads")
QgsProject.instance().addMapLayer(layer)Example 2: Diagnosing GeoPackage Sublayer Issues
from qgis.core import QgsVectorLayer, QgsDataProvider, QgsProject
def load_all_gpkg_layers(gpkg_path):
"""Load all layers from a GeoPackage with error handling."""
# First, open the file to inspect sublayers
temp_layer = QgsVectorLayer(gpkg_path, "temp", "ogr")
if not temp_layer.isValid():
raise RuntimeError(f"Cannot open GeoPackage: {gpkg_path}")
sublayers = temp_layer.dataProvider().subLayers()
if not sublayers:
print("GeoPackage contains no vector layers")
return []
loaded = []
for sublayer_info in sublayers:
parts = sublayer_info.split(QgsDataProvider.SUBLAYER_SEPARATOR)
layer_name = parts[1]
uri = f"{gpkg_path}|layername={layer_name}"
layer = QgsVectorLayer(uri, layer_name, "ogr")
if layer.isValid():
QgsProject.instance().addMapLayer(layer)
loaded.append(layer)
print(f"Loaded: {layer_name} ({layer.featureCount()} features)")
else:
print(f"FAILED: {layer_name}")
return loaded
layers = load_all_gpkg_layers("/data/project.gpkg")Example 3: PostGIS Connection with Fallback Diagnostics
from qgis.core import QgsDataSourceUri, QgsVectorLayer
def connect_postgis(host, port, dbname, user, password, schema, table, geom_col, key_col="gid"):
"""Connect to PostGIS with detailed error reporting."""
uri = QgsDataSourceUri()
uri.setConnection(host, port, dbname, user, password)
uri.setDataSource(schema, table, geom_col, "", key_col)
# ALWAYS use uri.uri(False) to prevent credential leaking
layer = QgsVectorLayer(uri.uri(False), f"{schema}.{table}", "postgres")
if not layer.isValid():
# Provide specific diagnostic guidance
dp = layer.dataProvider()
if dp is None:
print("DIAGNOSIS: PostgreSQL provider not available — check QGIS installation")
else:
error = dp.error().message()
if "could not connect" in error.lower():
print(f"DIAGNOSIS: Network issue — verify {host}:{port} is reachable")
elif "authentication failed" in error.lower():
print(f"DIAGNOSIS: Wrong credentials for user '{user}'")
elif "does not exist" in error.lower():
print(f"DIAGNOSIS: Table '{schema}.{table}' not found in database '{dbname}'")
elif "permission denied" in error.lower():
print(f"DIAGNOSIS: User '{user}' lacks SELECT on '{schema}.{table}'")
else:
print(f"DIAGNOSIS: {error}")
return None
return layer
layer = connect_postgis("localhost", "5432", "gisdb", "gisuser", "pass", "public", "parcels", "geom")Example 4: Fixing Shapefile Encoding
from qgis.core import QgsVectorLayer
def load_shapefile_with_encoding(shp_path, name, encoding="UTF-8"):
"""Load a Shapefile with explicit encoding for .dbf attributes."""
layer = QgsVectorLayer(shp_path, name, "ogr")
if not layer.isValid():
raise RuntimeError(f"Cannot load Shapefile: {shp_path}")
# Set encoding on the data provider
layer.dataProvider().setEncoding(encoding)
# Verify: read first feature's attributes
feature = next(layer.getFeatures(), None)
if feature:
for field in layer.fields():
value = feature[field.name()]
if isinstance(value, str):
print(f" {field.name()}: {value}")
return layer
# Try different encodings if text is garbled
for enc in ["UTF-8", "ISO-8859-1", "Windows-1252"]:
try:
layer = load_shapefile_with_encoding("/data/old_dutch_parcels.shp", "Parcels", enc)
print(f"Encoding {enc} appears correct")
break
except Exception as e:
print(f"Encoding {enc} failed: {e}")Example 5: Large Dataset with Spatial Filter
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsRectangle
def count_features_in_bbox(layer_path, bbox, provider="ogr"):
"""Efficiently count features within a bounding box without loading all data."""
layer = QgsVectorLayer(layer_path, "temp", provider)
if not layer.isValid():
raise RuntimeError(f"Cannot load: {layer_path}")
total = layer.featureCount()
print(f"Total features in dataset: {total}")
# Use QgsFeatureRequest to filter server-side
request = QgsFeatureRequest()
request.setFilterRect(bbox)
request.setFlags(QgsFeatureRequest.NoGeometry) # Skip geometry for counting
request.setSubsetOfAttributes([]) # No attributes needed
count = 0
for _ in layer.getFeatures(request):
count += 1
print(f"Features in bounding box: {count}")
return count
bbox = QgsRectangle(100000, 400000, 110000, 410000)
count_features_in_bbox("/data/huge_parcels.gpkg|layername=parcels", bbox)Example 6: WMS Layer Loading with Validation
from qgis.core import QgsRasterLayer, QgsProject
def load_wms_layer(service_url, layer_name, crs="EPSG:4326", img_format="image/png"):
"""Load a WMS layer with full error handling."""
uri = (
f"crs={crs}"
f"&format={img_format}"
f"&layers={layer_name}"
f"&styles"
f"&url={service_url}"
)
layer = QgsRasterLayer(uri, f"WMS: {layer_name}", "wms")
if not layer.isValid():
print(f"WMS FAILED — Checklist:")
print(f" 1. Is the URL reachable? {service_url}")
print(f" 2. Does layer '{layer_name}' exist in GetCapabilities?")
print(f" 3. Does the service support CRS {crs}?")
print(f" 4. Is the format {img_format} supported?")
return None
QgsProject.instance().addMapLayer(layer)
return layer
layer = load_wms_layer(
"https://geodata.nationaalgeoregister.nl/top10nlv2/wms",
"top10nlv2",
crs="EPSG:28992"
)Example 7: Assigning Missing CRS
from qgis.core import QgsVectorLayer, QgsCoordinateReferenceSystem, QgsProject
def load_with_crs_check(path, name, expected_crs="EPSG:4326"):
"""Load a layer and assign CRS if missing."""
layer = QgsVectorLayer(path, name, "ogr")
if not layer.isValid():
raise RuntimeError(f"Cannot load: {path}")
if not layer.crs().isValid():
print(f"Layer '{name}' has no CRS — assigning {expected_crs}")
crs = QgsCoordinateReferenceSystem(expected_crs)
if not crs.isValid():
raise RuntimeError(f"Invalid CRS: {expected_crs}")
layer.setCrs(crs)
elif layer.crs().authid() != expected_crs:
print(f"WARNING: Layer CRS is {layer.crs().authid()}, expected {expected_crs}")
QgsProject.instance().addMapLayer(layer)
return layer
layer = load_with_crs_check("/data/old_survey.shp", "Survey", "EPSG:28992")Example 8: GeoPackage Lock Detection and Recovery
import os
from qgis.core import QgsVectorLayer
def load_gpkg_safe(gpkg_path, layer_name):
"""Load a GeoPackage layer with lock file detection."""
# Check for stale lock files
lock_extensions = ["-wal", "-shm", "-journal"]
locks_found = []
for ext in lock_extensions:
lock_path = gpkg_path + ext
if os.path.exists(lock_path):
locks_found.append(lock_path)
if locks_found:
print(f"WARNING: Lock files detected for {gpkg_path}:")
for lf in locks_found:
size = os.path.getsize(lf)
print(f" {lf} ({size} bytes)")
print("Another process may be writing to this file.")
uri = f"{gpkg_path}|layername={layer_name}"
layer = QgsVectorLayer(uri, layer_name, "ogr")
if not layer.isValid():
if locks_found:
print("DIAGNOSIS: Lock files present — close other QGIS instances or database tools")
raise RuntimeError(f"Cannot load {layer_name} from {gpkg_path}")
return layer
layer = load_gpkg_safe("/data/project.gpkg", "buildings")qgis-errors-data-loading — Methods Reference
Layer Construction
QgsVectorLayer
QgsVectorLayer(path: str, baseName: str = "", providerLib: str = "", options: QgsVectorLayer.LayerOptions = QgsVectorLayer.LayerOptions()) -> QgsVectorLayerpath— Data source URI (format depends on provider)baseName— Display name in the layer treeproviderLib— Provider identifier:"ogr","postgres","memory","WFS","delimitedtext","spatialite","gpx","virtual"- Returns a layer object that MUST be checked with
isValid()
QgsRasterLayer
QgsRasterLayer(path: str = "", baseName: str = "", providerType: str = "", options: QgsRasterLayer.LayerOptions = QgsRasterLayer.LayerOptions()) -> QgsRasterLayerproviderType— Provider identifier:"gdal","wms","wcs","postgresraster"- Returns a layer object that MUST be checked with
isValid()
---
Layer Validity
QgsMapLayer.isValid()
layer.isValid() -> boolReturns True if the layer was loaded successfully. ALWAYS call immediately after construction.
QgsMapLayer.error()
layer.error() -> QgsErrorReturns the error object for the layer. Use error().message() to get a human-readable string.
---
Data Provider Error Inspection
QgsDataProvider.error()
layer.dataProvider().error() -> QgsErrorReturns detailed error information from the data provider. More specific than layer.error().
QgsError.message()
error.message() -> strReturns the error message as a formatted string including all error components.
QgsError.isEmpty()
error.isEmpty() -> boolReturns True if no error was recorded.
---
QgsDataSourceUri (Database URI Builder)
Connection Methods
uri = QgsDataSourceUri()
uri.setConnection(host: str, port: str, database: str, username: str, password: str) -> None
uri.setConnection(host: str, port: str, database: str, username: str, password: str, sslmode: QgsDataSourceUri.SslMode) -> NoneData Source Methods
uri.setDataSource(schema: str, table: str, geometryColumn: str, sql: str = "", keyColumn: str = "") -> Noneschema— Database schema (e.g.,"public")table— Table or view namegeometryColumn— Name of the geometry columnsql— Optional WHERE clause filterkeyColumn— Primary key column (ALWAYS specify for views)
Authentication
uri.setAuthConfigId(authcfg: str) -> NoneUses a stored authentication configuration from QgsAuthManager. ALWAYS prefer this over plain passwords.
URI Generation
uri.uri(expandAuthConfig: bool = True) -> str- Pass
Falseto prevent credential expansion in the output string - ALWAYS use
uri.uri(False)when logging, displaying, or passing to layer constructors
---
Feature Request (Performance Optimization)
QgsFeatureRequest
request = QgsFeatureRequest()
# Spatial filter
request.setFilterRect(rect: QgsRectangle) -> QgsFeatureRequest
# Attribute filter
request.setFilterExpression(expression: str) -> QgsFeatureRequest
# Feature ID filter
request.setFilterFids(fids: set) -> QgsFeatureRequest
# Limit returned attributes
request.setSubsetOfAttributes(attrs: list, fields: QgsFields) -> QgsFeatureRequest
# Skip geometry loading
request.setFlags(QgsFeatureRequest.NoGeometry) -> QgsFeatureRequest
# Limit result count
request.setLimit(limit: int) -> QgsFeatureRequest---
CRS Methods
QgsMapLayer.crs()
layer.crs() -> QgsCoordinateReferenceSystemReturns the layer's coordinate reference system.
QgsMapLayer.setCrs()
layer.setCrs(crs: QgsCoordinateReferenceSystem) -> NoneAssigns CRS metadata to the layer. Does NOT reproject coordinates.
QgsCoordinateReferenceSystem.isValid()
crs.isValid() -> boolReturns True if the CRS is a recognized, valid coordinate reference system.
QgsCoordinateReferenceSystem.authid()
crs.authid() -> strReturns the authority identifier (e.g., "EPSG:4326").
---
Encoding Methods
QgsVectorDataProvider.setEncoding()
layer.dataProvider().setEncoding(encoding: str) -> NoneSets the character encoding for reading attribute data. Common values: "UTF-8", "ISO-8859-1", "Windows-1252".
---
Sublayer Inspection
QgsDataProvider.subLayers()
layer.dataProvider().subLayers() -> list[str]Returns a list of sublayer descriptions for multi-layer data sources (e.g., GeoPackage, GDB). Each string contains fields separated by QgsDataProvider.SUBLAYER_SEPARATOR.
QgsDataProvider.SUBLAYER_SEPARATOR
QgsDataProvider.SUBLAYER_SEPARATOR # Constant string used to split sublayer info