Handle Wikidata API errors with proper HTTP responses (429, 503)

Also retry on RequestException and raise QueryError on non-200 responses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2026-05-15 15:19:02 +00:00
parent d75d617d11
commit 1442620ece
2 changed files with 15 additions and 2 deletions

View file

@ -110,13 +110,16 @@ Row = dict[str, dict[str, typing.Any]]
Hit = dict[str, str | int | None]
@backoff.on_exception(backoff.expo, QueryError, max_tries=5)
@backoff.on_exception(backoff.expo, (QueryError, RequestException), max_tries=5)
def wdqs(query: str) -> list[Row]:
"""Pass query to the Wikidata Query Service."""
r = requests.post(
wikidata_query_api_url, data={"query": query, "format": "json"}, headers=headers
)
if not r.ok:
raise QueryError(query, r)
try:
return typing.cast(list[Row], r.json()["results"]["bindings"])
except requests.exceptions.JSONDecodeError:

View file

@ -10,6 +10,7 @@ import traceback
import typing
from time import time
import requests.exceptions
import sqlalchemy.exc
import werkzeug.debug.tbtools
from flask import Flask, jsonify, redirect, render_template, request, url_for
@ -375,7 +376,16 @@ def index() -> str | Response:
return jsonify(coords={"lat": lat, "lon": lon}, error=error_msg)
needs_commons = request.args.get("needs_commons", "true").lower() != "false"
result = lat_lon_to_wikidata(lat, lon, needs_commons=needs_commons)["result"]
try:
result = lat_lon_to_wikidata(lat, lon, needs_commons=needs_commons)["result"]
except (wikidata.QueryError, wikidata.APIResponseError) as e:
r = e.r if isinstance(e, wikidata.QueryError) else e.response
if r.status_code == 429:
extra = {"Retry-After": r.headers["Retry-After"]} if "Retry-After" in r.headers else {}
return jsonify(error="Rate limited by Wikidata, please try again later"), 429, extra
return jsonify(error=f"Wikidata query failed (HTTP {r.status_code}), please try again later"), 503
except requests.exceptions.RequestException:
return jsonify(error="Could not connect to Wikidata, please try again later"), 503
result.pop("element", None)
result.pop("geojson", None)
if logging_enabled: