Skip to content

Fix: Add error handling for missing 'tiles' key in stac_tile response#1282

Merged
giswqs merged 5 commits intomasterfrom
fix/stac-tile-error-handling
Jan 25, 2026
Merged

Fix: Add error handling for missing 'tiles' key in stac_tile response#1282
giswqs merged 5 commits intomasterfrom
fix/stac-tile-error-handling

Conversation

@giswqs
Copy link
Copy Markdown
Member

@giswqs giswqs commented Jan 25, 2026

Description

The NAIP imagery notebook was failing with KeyError: 'tiles' when trying to visualize search results. This was caused by two issues:

  1. Root cause: When stac_stats fails for Planetary Computer items, it was overwriting the titiler_endpoint variable with a fallback string endpoint. This caused the subsequent tile request to use the wrong endpoint path (/stac/ instead of /item/), which expects a url parameter instead of collection+item parameters.

  2. Symptom: When the wrong endpoint returned an error response, the code tried to access r['tiles'][0] without checking if the tiles key existed, resulting in an unhelpful KeyError.

Changes

  1. Fix endpoint overwriting: Use a separate fallback_endpoint variable for the stac_stats fallback, preserving the original titiler_endpoint for the main tile request
  2. Add error handling: Check if 'tiles' exists in the response before accessing it
  3. Improve error messages: Provide informative error messages based on the API response structure (dict errors with 'detail', list validation errors, or generic unexpected response)

Testing

Tested with the geo conda environment using:

  • The actual download_naip.ipynb notebook scenario
  • Direct stac_tile calls with Planetary Computer NAIP items
  • Verified tiles are successfully returned after the fix

Example

Before:

KeyError: 'tiles'

After:

tiles = stac_tile(
    collection="naip",
    item="nd_m_4709963_se_14_060_20230701_20231116",
    assets=["image"],
    titiler_endpoint="planetary-computer"
)
# Returns: 'https://planetarycomputer.microsoft.com/api/data/v1/item/tiles/...'

Related Issues

Fixes opengeos/geoai#468

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds defensive handling in stac_tile() when the TiTiler tilejson response does not include a "tiles" key, replacing an unhelpful KeyError with a more descriptive exception.

Changes:

  • Detects missing "tiles" in the TiTiler response before indexing.
  • Attempts to extract a helpful error message from common error response shapes.
  • Raises a ValueError with details instead of failing with KeyError.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread leafmap/stac.py
if "tiles" not in r:
# Try to provide helpful error message from the API response
if "detail" in r:
error_msg = f"TiTiler endpoint error: {r['detail']}"
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The common FastAPI/TiTiler validation error shape is a dict with a "detail" key whose value is a list of error dicts (e.g., {"detail": [{"loc": ..., "msg": ...}]}). In that case this branch will raise ValueError with the raw list repr, and the more user-friendly per-field formatting below is never used. Consider detecting isinstance(r.get('detail'), list) and formatting that list similarly (or delegating to a shared formatter) before falling back to str(r['detail']).

Suggested change
error_msg = f"TiTiler endpoint error: {r['detail']}"
detail = r.get("detail")
# Common FastAPI/TiTiler shape: {"detail": [{"loc": ..., "msg": ...}, ...]}
if isinstance(detail, list) and len(detail) > 0 and isinstance(detail[0], dict) and "msg" in detail[0]:
errors = []
for e in detail:
loc = "->".join(str(x) for x in e.get("loc", []))
msg = e.get("msg", "")
errors.append(f"{loc}: {msg}")
error_msg = f"TiTiler endpoint validation failed: {'; '.join(errors)}"
else:
error_msg = f"TiTiler endpoint error: {detail}"

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Jan 25, 2026

@github-actions github-actions bot temporarily deployed to pull request January 25, 2026 06:00 Inactive
When stac_stats fails for Planetary Computer items, it was overwriting
the titiler_endpoint variable with a fallback string endpoint. This caused
the subsequent tile request to use the wrong endpoint path (/stac/ instead
of /item/), which expects different parameters and returns errors.

Changes:
1. Use a separate fallback_endpoint variable for stac_stats fallback
2. Add error handling to check for 'tiles' key before accessing it
3. Provide informative error messages based on API response type

Fixes opengeos/geoai#468
@giswqs giswqs force-pushed the fix/stac-tile-error-handling branch from 5d74ca5 to ebcb497 Compare January 25, 2026 06:03
@github-actions github-actions bot temporarily deployed to pull request January 25, 2026 06:09 Inactive
giswqs and others added 4 commits January 25, 2026 01:29
The Planetary Computer /item/info.geojson endpoint returns a GeoJSON Feature
with bounds nested in properties.{asset}.bounds, not a top-level bbox field.

This fix:
- Checks for bounds in properties.{asset}.bounds format
- Falls back to calculating bbox from geometry coordinates
- Ensures map zooms to correct location when visualizing NAIP items

Fixes opengeos/geoai#468
When stac_stats fails (e.g., for Planetary Computer items that don't
support the statistics endpoint), the errors were being printed to stdout,
cluttering the output.

This fix:
- Wraps both primary and fallback stac_stats calls in try-except
- Silently continues without rescaling if stats are unavailable
- Removes the print(stats['detail']) statement
- Sets stats=None if both attempts fail

The tile visualization works fine without stats - they're only used for
automatic rescaling, which isn't critical for display.

Fixes opengeos/geoai#468
The real bug: check_titiler_endpoint was calling .lower() on the endpoint
parameter without checking if it was a string first. When stac_stats was
called with a PlanetaryComputerEndpoint object, it would pass it through
check_titiler_endpoint again, which would fail trying to call .lower() on
the object.

This caused stac_stats to fail and fall back to the giswqs endpoint, which
then printed error messages.

The fix:
- Check isinstance(titiler_endpoint, str) before calling .lower()
- Allow endpoint objects to pass through unchanged
- Now stac_stats works correctly with Planetary Computer from the start
- No more fallback needed, no more error messages
- Stats are properly retrieved and used for automatic rescaling

This is the proper fix instead of just silencing errors.

Fixes opengeos/geoai#468
@giswqs giswqs merged commit 9648807 into master Jan 25, 2026
15 checks passed
@giswqs giswqs deleted the fix/stac-tile-error-handling branch January 25, 2026 06:43
@github-actions github-actions bot temporarily deployed to pull request January 25, 2026 06:44 Inactive
giswqs added a commit that referenced this pull request Jan 27, 2026
…#1282)

* Fix: Prevent titiler_endpoint from being overwritten in stac_tile

When stac_stats fails for Planetary Computer items, it was overwriting
the titiler_endpoint variable with a fallback string endpoint. This caused
the subsequent tile request to use the wrong endpoint path (/stac/ instead
of /item/), which expects different parameters and returns errors.

Changes:
1. Use a separate fallback_endpoint variable for stac_stats fallback
2. Add error handling to check for 'tiles' key before accessing it
3. Provide informative error messages based on API response type

Fixes opengeos/geoai#468

* Fix stac_bounds to handle GeoJSON Feature format from Planetary Computer

The Planetary Computer /item/info.geojson endpoint returns a GeoJSON Feature
with bounds nested in properties.{asset}.bounds, not a top-level bbox field.

This fix:
- Checks for bounds in properties.{asset}.bounds format
- Falls back to calculating bbox from geometry coordinates
- Ensures map zooms to correct location when visualizing NAIP items

Fixes opengeos/geoai#468

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Silently handle stac_stats failures instead of printing errors

When stac_stats fails (e.g., for Planetary Computer items that don't
support the statistics endpoint), the errors were being printed to stdout,
cluttering the output.

This fix:
- Wraps both primary and fallback stac_stats calls in try-except
- Silently continues without rescaling if stats are unavailable
- Removes the print(stats['detail']) statement
- Sets stats=None if both attempts fail

The tile visualization works fine without stats - they're only used for
automatic rescaling, which isn't critical for display.

Fixes opengeos/geoai#468

* Fix check_titiler_endpoint to handle endpoint objects properly

The real bug: check_titiler_endpoint was calling .lower() on the endpoint
parameter without checking if it was a string first. When stac_stats was
called with a PlanetaryComputerEndpoint object, it would pass it through
check_titiler_endpoint again, which would fail trying to call .lower() on
the object.

This caused stac_stats to fail and fall back to the giswqs endpoint, which
then printed error messages.

The fix:
- Check isinstance(titiler_endpoint, str) before calling .lower()
- Allow endpoint objects to pass through unchanged
- Now stac_stats works correctly with Planetary Computer from the start
- No more fallback needed, no more error messages
- Stats are properly retrieved and used for automatic rescaling

This is the proper fix instead of just silencing errors.

Fixes opengeos/geoai#468

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KeyError: 'tiles' in stac_tile when running Download NAIP imagery example notebook

2 participants