Error Handling
AIREloom uses a hierarchy of custom exceptions to signal various issues that can occur during API interactions, configuration, or client-side validation. Understanding these exceptions is key to building robust applications.
All AIREloom-specific exceptions inherit from BibliofabricError.
Exception Hierarchy and Details
Here are the main exceptions you might encounter:
-
BibliofabricError(Exception):- The base class for all errors raised by the AIREloom library.
- Attributes:
message(str): The primary error message.response(Optional[httpx.Response]): Thehttpx.Responseobject if the error is related to an API response.request(Optional[httpx.Request]): Thehttpx.Requestobject associated with the error, if available.
-
APIError(BibliofabricError):- Represents a generic error returned by the OpenAIRE API, typically corresponding to non-success HTTP status codes (4xx or 5xx) that are not covered by more specific exceptions below.
- Inherits
message,response, andrequestfromBibliofabricError.
-
NotFoundError(APIError):- A specific type of
APIErrorraised when the API returns a404 Not Foundstatus, indicating that the requested resource does not exist.
- A specific type of
-
ValidationError(BibliofabricError):- Raised for several reasons:
- Client-side validation failure before sending a request (e.g., invalid filter parameters, incorrect sort field format).
- API response indicating a validation error, often a
400 Bad Requestor422 Unprocessable Entity.
- The
responseattribute might be present if the error originated from an API response.
- Raised for several reasons:
-
RateLimitError(APIError):- A specific type of
APIErrorraised when the API returns a429 Too Many Requestsstatus, indicating that your application has exceeded its allocated rate limit. - See the Rate Limiting Guide for how AIREloom handles these.
- A specific type of
-
TimeoutError(BibliofabricError):- Raised when a request to the API times out after exhausting configured retries.
- The
responseattribute will typically beNone. - The
requestattribute will contain thehttpx.Requestobject that timed out.
-
NetworkError(BibliofabricError):- Raised for network-level issues that prevent communication with the API, such as DNS resolution failures, connection refused, or other
httpx.NetworkErrorsubtypes. - The
responseattribute will typically beNone. - The
requestattribute will contain thehttpx.Requestobject that failed.
- Raised for network-level issues that prevent communication with the API, such as DNS resolution failures, connection refused, or other
-
ConfigurationError(BibliofabricError):- Raised if there's an issue with the AIREloom client's configuration (e.g., missing required settings for an authentication strategy).
- Typically does not have
responseorrequestattributes.
-
AuthError(BibliofabricError):- Raised when an authentication-specific error occurs. This could be due to:
- Failure to obtain an OAuth2 token (e.g., invalid client credentials).
- An API response indicating an authentication or authorization failure (e.g.,
401 Unauthorized,403 Forbidden).
- The
responseattribute may be present if the error was triggered by an API response.
- Raised when an authentication-specific error occurs. This could be due to:
Handling Exceptions
It's crucial to wrap your AIREloom API calls in try...except blocks to gracefully handle potential errors. You can catch specific exceptions or the general BibliofabricError.
import asyncio
import httpx # For type hinting if needed
from aireloom import AireloomSession
from bibliofabric.auth import NoAuth # Or your preferred auth strategy
from bibliofabric.exceptions import (
BibliofabricError,
APIError,
NotFoundError,
ValidationError,
RateLimitError,
TimeoutError,
NetworkError,
AuthError,
ConfigurationError
)
# Assuming ResearchProductsFilters is imported for a search example
from aireloom.endpoints import ResearchProductsFilters
async def fetch_data_example():
# Using NoAuth for simplicity in this example.
# Ensure your auth strategy is correctly configured for actual use.
async with AireloomSession(auth_strategy=NoAuth()) as session:
try:
# Example 1: Fetching a single entity
print("Attempting to fetch a specific product...")
# Replace with a valid ID for testing, or an invalid one to trigger NotFoundError
product = await session.research_products.get("openaire____::doi:10.5281/zenodo.7664304")
print(f"Fetched product: {product.title}")
# Example 2: Searching with potentially invalid filters
print("\nAttempting a search...")
# To trigger ValidationError client-side, pass an invalid filter type:
# invalid_filters = "this_is_not_a_filter_model"
# Or use a filter model with invalid field values if server-side validation is targeted
search_filters = ResearchProductsFilters(title="modern research trends", publicationYear="2023")
search_results = await session.research_products.search(filters=search_filters, page_size=2)
print(f"Found {search_results.header.total} search results.")
except NotFoundError as e:
print(f"Resource not found: {e.message}")
if e.request:
print(f" Request URL: {e.request.url}")
except ValidationError as e:
print(f"Validation error: {e.message}")
if e.response:
print(f" API Response Status: {e.response.status_code}")
# You might want to log e.response.text for more details from the API
print(f" API Response Text: {e.response.text[:200]}...")
if e.request:
print(f" Request URL: {e.request.url}")
except RateLimitError as e:
print(f"Rate limit exceeded: {e.message}")
if e.response and "Retry-After" in e.response.headers:
print(f" Suggested Retry-After: {e.response.headers['Retry-After']} seconds")
# Implement your own delay or backoff strategy if needed beyond client's retries
except TimeoutError as e:
print(f"Request timed out: {e.message}")
if e.request:
print(f" Timed out request URL: {e.request.url}")
except NetworkError as e:
print(f"Network error: {e.message}")
if e.request:
print(f" Failed request URL: {e.request.url}")
except AuthError as e:
print(f"Authentication error: {e.message}")
# Check e.response for details if it's an API auth error
except APIError as e: # Catch other API errors (e.g., 500 Internal Server Error)
print(f"Generic API error: {e.message}")
if e.response:
print(f" Status: {e.response.status_code}, URL: {e.request.url if e.request else 'N/A'}")
print(f" Response: {e.response.text[:200]}...")
except ConfigurationError as e:
print(f"Configuration error: {e.message}")
except BibliofabricError as e: # Catch-all for any other Aireloom specific errors
print(f"An Aireloom error occurred: {e.message}")
if e.response:
print(f" Status: {e.response.status_code}, URL: {e.request.url if e.request else 'N/A'}")
elif e.request:
print(f" Request URL: {e.request.url}")
except Exception as e: # Catch any other unexpected errors
print(f"An unexpected non-Aireloom error occurred: {e}")
if __name__ == "__main__":
asyncio.run(fetch_data_example())
Best Practices
- Be Specific: Catch the most specific exceptions you anticipate first (e.g.,
NotFoundError,RateLimitError), followed by more general ones likeAPIError, and finallyBibliofabricError. - Inspect
responseandrequest: For errors likeAPIError, theresponseattribute can provide valuable details from the API (status code, headers, body). Therequestattribute helps identify which call failed. - Logging: In a production application, log detailed error information, including stack traces and the content of
requestandresponseobjects, to help diagnose issues. - User Feedback: Provide clear feedback to users when errors occur, especially for issues like timeouts or resources not being found.
- Retry Strategies: While AIREloom has built-in retries for transient errors and rate limits, you might implement additional application-level retry logic for certain scenarios, perhaps with longer or more complex backoff strategies.
By effectively handling these exceptions, you can create more resilient and user-friendly applications that interact with OpenAIRE APIs.