Troubleshooting

Some information and solutions to common problems.

SSL: CERTIFICATE_VERIFY_FAILED

ℹ️

This usually indicates that the outgoing network 'handshake' did not work.

This occurs if your system wasn’t able to securely connect to our server because it couldn’t verify our SSL certificate. This is a standard internet security check, and it usually means that your computer is missing a required security certificate (called a "root certificate") that helps confirm our site is safe.

This can happen if:

  • You're using an older operating system or Python environment without updated certificates.
  • Your network (e.g. a corporate firewall or proxy) is interfering with certificate checks.
  • Your system’s SSL certificate store is out of date.

There are several possible fixes:

  1. (Easiest) Add verify=false as an argument in the get function.
    1. In the script, find this line: response = requests.get(url, headers=config.headers)
    2. Modify to: response = requests.get(url, headers=config.headers, verify=False)
    3. NB This option is recommended in secure development settings only.
  2. Use HTTP instead of HTTP. HTTP is the standard way websites communicate, while HTTPS adds encryption to protect your data and ensure secure, private connections.
    1. In the script, find: BASE_URL = "https://api.renewmap.com.au/api/v1/files/esri/shp/projects.zip"
    2. Modify to BASE_URL = "http://api.renewmap.com.au/api/v1/files/esri/shp/projects.zip"
    3. NB This option is recommended in secure development settings only.
  3. If you're using Python, try updating your certifi package
    1. Run pip install --upgrade certifi
  4. (Optional) Check the path to the certifi packages
    1. Run python -m certifi in your environment terminal and check the output, e.g. C:\path\to\cacert.pem
    2. Then set the environment variable REQUESTS_CA_BUNDLE to the certifi package path:
      set REQUESTS_CA_BUNDLE=C:\path\to\cacert.pem
  5. Make sure your operating system is up to date.
  6. If you’re behind a firewall or proxy, try connecting from another network or ask your IT team if certificate verification might be blocked.

In ArcGIS Pro

If you are running in ArcGIS Pro, there may be complications because Pro often ships with older versions of Python. Try running the script in a non-ArcGIS Pro environment if you have one. This will ensure that you're running on a more recent Python version and the SSL cert handling may be better.

  1. Install VSCode
  2. Install the Python extension
  3. Run the script

If this is not possible, try updating the ArcGIS Python packages according to the documentation.


407 Proxy Authentication Required

ℹ️

Your system is trying to send requests through a proxy server that requires login credentials, but those credentials were not provided or were rejected.

An outgoing proxy server is often used by organizations to control and monitor internet traffic from internal systems. It acts as a middleman between your computer and the internet, requiring authentication in some cases to ensure only authorized users can access external resources.

QGIS

Adding Proxy authentication details to QGIS

⚠️

Proxy server authentication details are sensitive. Please don't share your details.

  1. Set QGIS to use proxy for web access. Try the default option first, which applies the system proxy settings

  2. Select OK and then run the script below Test proxy code in the Python console script editor.

  3. If Step 2 doesn't work, try using a different proxy type, eg HttpProxy.  You will also need to add credentials. You have two options for adding authentication details:

    1. The Configurations tab stores authentication details in a back end QGIS database. You have more options, and this method is slightly more secure.

    2. The Basic tab stores your authentication username and password as text strings in the QGIS settings.

  4. It may take a bit of trial and error to get the correct configurations. Try different configurations in the authenticator and attempt the Test proxy code each time to see if you can get a successful response.

If the test code works, try adding the RenewMap Projects API using the RenewMap Projects API with proxy handling below.

Test proxy code

Use this script to check whether QGIS is able to connect to external websites and return data. It returns data from a publicly available test API at https://jsonplaceholder.typicode.com/todos/1. If this script succeeds, you will see an output with your applied proxy settings, and the Response data from the test API (see the Output on success tab below).

To run, paste this code in a script in the Python plugin script editor in QGIS, and hit Run ▶️

"""
This script requests data from a test API at jsonplaceholder.typicode.com
It first gets the proxy settings that have been applied in the QGIS UI.
 
Author: [email protected]
Date: 5/02/2025
"""
 
from PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from qgis.core import QgsNetworkAccessManager
 
# Define URL
url = "https://jsonplaceholder.typicode.com/todos/1"
 
# Create network request
request = QNetworkRequest(QUrl(url))
 
# Get QGIS's network manager
nam = QgsNetworkAccessManager.instance()
# Apply the proxy details from QGIS settings
nam.setupDefaultProxyAndCache()
proxy = nam.fallbackProxy()
 
print("✅ Proxy Settings:")
print(f"Host: {proxy.hostName()}")
print(f"Port: {proxy.port()}")
print(f"User: {proxy.user()}")
print(f"Password: {'(hidden)' if proxy.password() else '(none)'}")
print(f"Proxy Type: {proxy.type()}")
 
# Send the request
reply = nam.get(request)
 
# Wait for the reply to finish in a **non-blocking** way
while not reply.isFinished():
    QCoreApplication.processEvents()  # Allows QGIS to continue running
 
# Check for errors
if reply.error() == QNetworkReply.NoError:
    print("\n", "✅ Response:", reply.readAll().data().decode())
else:
    print(f"❌ Request failed: {reply.errorString()}")
 
# Clean up the reply object
reply.deleteLater()
✅ Proxy Settings:
Host: ...
Port: ...
User: ...
Password: ... (hidden)
Proxy Type: ...

✅ Response: {
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

Then , in any of the API scripts, replace fetch_data() with the code below.

def fetch_data(config: APIConfig) -> pd.DataFrame:
    """
    Fetch data from API using QGIS's network manager (which honors QGIS proxy settings).

    Args:
        config: APIConfig object containing API configuration.

    Returns:
        DataFrame containing all fetched data.
    """
    data_list = []
    offset = 0

    # Get QGIS's network manager and apply the proxy settings from QGIS
    nam = QgsNetworkAccessManager.instance()
    nam.setupDefaultProxyAndCache()
    
    while True:
        # Build the URL with pagination parameters
        url = config.get_url(offset)
        print(f"Fetching: {url}")
        
        # Create the network request
        request = QNetworkRequest(QUrl(url))
        # Add headers (QNetworkRequest requires byte strings for header names and values)
        for header_name, header_value in config.headers.items():
            request.setRawHeader(header_name.encode('utf-8'), header_value.encode('utf-8'))
            print(f"{header_name}: {header_value}")
        # Send the request via QGIS's network manager
        reply = nam.blockingGet(request)

        # Check for errors
        if reply.error() != QNetworkReply.NoError:
            error_msg = reply.errorString()
            raise Exception(error_msg)

        # Read and decode the response data
        response_bytes = reply.content()  # This returns a QByteArray
        response_str = response_bytes.data().decode("utf-8")

        # Convert response JSON string into a Python dictionary
        data_dict = json.loads(response_str)
        batch = data_dict[config.data_key]

        if len(batch) == 0:
            break  # No more data available
        
        data_list.extend(batch)
        offset += config.limit  # Increment offset for next request

    # Flatten the fields data if applicable
    if config.add_fields:
        data_list = [flatten_fields(item) for item in data_list]

    print(f"Fetched {len(data_list)} items")
    return pd.DataFrame(data_list)