From 1b955139046146c5cbff30f598399e1564ba7f69 Mon Sep 17 00:00:00 2001 From: Hafiz Hussain Date: Thu, 23 Jul 2026 16:04:19 +0500 Subject: [PATCH] Don't crash map creation when Google Maps basemaps fail to load (#2701) When GOOGLE_MAPS_API_KEY is set, geemap eagerly creates Google Maps tile providers, which POST to the Maps Tiles createSession endpoint. For accounts/regions without Tiles API access this returns 403 and raises, which propagated out of _get_available_basemaps() and crashed Map() creation even when the user never requested Google tiles. Wrap the Google basemap loading in a try/except that logs a warning and continues with the standard basemaps, in both geemap.core.Map and geemap.maplibregl.Map. Adds a regression test that simulates the failure and asserts the map is still created with basemaps available and a warning emitted. --- geemap/core.py | 15 ++++++++++++--- geemap/maplibregl.py | 16 +++++++++++++--- tests/test_core.py | 25 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/geemap/core.py b/geemap/core.py index 1dfc2ff2ea..30c065b2bc 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -1351,9 +1351,18 @@ def _get_available_basemaps(self) -> dict[str, Any]: """ tile_providers = list(basemaps.get_xyz_dict().values()) if coreutils.get_google_maps_api_key(): - tile_providers = tile_providers + list( - basemaps.get_google_map_tile_providers().values() - ) + try: + tile_providers = tile_providers + list( + basemaps.get_google_map_tile_providers().values() + ) + except Exception as e: + logging.warning( + "Unable to load Google Maps basemaps: %s. Continuing without " + "them. Unset the GOOGLE_MAPS_API_KEY environment variable if " + "your account or region does not support the Google Maps " + "Tiles API.", + e, + ) ret_dict = {} for tile_info in tile_providers: diff --git a/geemap/maplibregl.py b/geemap/maplibregl.py index 2a44777b76..19e35f5e9d 100644 --- a/geemap/maplibregl.py +++ b/geemap/maplibregl.py @@ -5,6 +5,7 @@ import base64 import glob import importlib.resources +import logging import os import re from typing import Any @@ -169,9 +170,18 @@ def _get_available_basemaps(self) -> dict[str, Any]: """Convert xyz tile services to a dictionary of basemaps.""" tile_providers = list(get_xyz_dict().values()) if coreutils.get_google_maps_api_key(): - tile_providers = tile_providers + list( - get_google_map_tile_providers().values() - ) + try: + tile_providers = tile_providers + list( + get_google_map_tile_providers().values() + ) + except Exception as e: + logging.warning( + "Unable to load Google Maps basemaps: %s. Continuing without " + "them. Unset the GOOGLE_MAPS_API_KEY environment variable if " + "your account or region does not support the Google Maps " + "Tiles API.", + e, + ) ret_dict = {} for tile_info in tile_providers: diff --git a/tests/test_core.py b/tests/test_core.py index 1d868a39ab..f7373202a8 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -57,6 +57,31 @@ def test_defaults(self): self.assertIsInstance(controls[5], ipyleaflet.ScaleControl) self.assertIsInstance(controls[6], ipyleaflet.AttributionControl) + def test_available_basemaps_survive_google_maps_error(self): + """Map creation should not fail when Google Maps basemaps can't load. + + Regression test for #2701: having GOOGLE_MAPS_API_KEY set should not + crash map creation if the account/region can't access the Tiles API. + """ + with ( + mock.patch.object( + core.coreutils, "get_google_maps_api_key", return_value="fake-key" + ), + mock.patch.object( + core.basemaps, + "get_google_map_tile_providers", + side_effect=RuntimeError("Error creating a Maps API session"), + ), + ): + with self.assertLogs(level="WARNING") as logged: + new_map = core.Map(ee_initialize=False) + + # Standard basemaps are still available despite the Google Maps failure. + self.assertGreater(len(new_map._available_basemaps), 0) + self.assertTrue( + any("Unable to load Google Maps" in message for message in logged.output) + ) + def test_set_center(self): """Tests that `set_center` sets the center and zoom.""" self.core_map.set_center(1, 2, 3)