Handle API errors
This guide shows you how to catch the exceptions that kroger-api raises and decide what to do about each one. For the full list of classes and attributes, see the errors reference.
Catch errors by type
Catch the most specific class you can act on, then KrogerAPIError for everything else. Every exception the library raises about a request derives from it.
from kroger_api import KrogerAPI, KrogerAPIError, InvalidParameterError, NotFoundError
kroger = KrogerAPI()
kroger.authorization.get_token_with_client_credentials("product.compact")
location_id = "99999999"
try:
store = kroger.location.get_location(location_id)
except InvalidParameterError as e:
print(f"That store ID {e.requirement}.")
except NotFoundError:
print(f"No store has the ID {location_id}.")
except KrogerAPIError as e:
print(e)
InvalidParameterError means the client found a problem with an argument and sent no request. Its requirement says what a valid value looks like, for example must be an 8-character store locationId such as '01400513'.
Show what went wrong
The message of each exception is complete enough to show to a developer as-is. For an API error, it holds the HTTP status, Kroger's explanation and error code, the request, the correlation ID, and a hint when the cause is known:
Kroger API error 404 Not Found: (no error message in the response body)
request: GET /v1/locations/99999999
correlation id: akam_1792607141_ec02f117-d8d0-46c9-8dbe-ba9a40b637ab
hint: No store has this locationId. Find valid IDs with a location search.
To build your own message, read the attributes instead: status_code, kroger_code, kroger_message, hint, and the others in the API error attributes table.
Retry only what a retry can fix
A retry helps only when the failure is temporary. Retry a network failure or a 5xx response once, after a short wait, unless the error's outcome_unknown is True. In that case a cart add may have gone through, and a retry could add the items twice. Do not retry other errors: the same request fails the same way.
import time
from kroger_api import APIConnectionError, InternalServerError
def call_with_one_retry(call, delay_seconds=5):
try:
return call()
except (APIConnectionError, InternalServerError) as e:
if e.outcome_unknown:
raise # the request may have been processed: check before sending it again
time.sleep(delay_seconds)
return call()
products = call_with_one_retry(
lambda: kroger.product.search_products(term="milk", location_id="01400513")
)
APIConnectionError also covers APITimeoutError. The outcome reference lists when outcome_unknown is True. The client already handles one case of 401 itself: it gets a new token and resends the request once, as the 401 recovery section describes.
| Error | What to do |
|---|---|
InvalidParameterError, BadRequestError | Correct the argument that param or kroger_message names. |
AuthenticationError from a cart call | Sign the user in again with the authorization-code flow. |
AuthenticationError from the token endpoint | Correct KROGER_CLIENT_ID and KROGER_CLIENT_SECRET. |
PermissionDeniedError | Use a token with the scope the endpoint needs. Cart calls need a user token. |
NotFoundError | Look up a valid ID with a search or list call. |
RateLimitError | Stop and resume after the daily limit resets. See Handle rate limits and pagination. |
InternalServerError, APIConnectionError | Retry once after a short wait. If outcome_unknown is True, have the user check the cart first. |
ConfigurationError | Set the missing environment variable or argument. |
Log the details
Log to_dict() to keep every detail in a form that log tools can search. Include the correlation ID when you report a problem to Kroger: it identifies the request in their logs.
import json
import logging
try:
kroger.cart.add_to_cart([{"upc": "0001111041700", "quantity": 1, "modality": "PICKUP"}])
except KrogerAPIError as e:
logging.error("Kroger API call failed: %s", json.dumps(e.to_dict()))
raise
The client also logs what it does about tokens, such as refreshing an expired one, through the kroger_api logger. Warnings, such as a rejected refresh token, appear by default. To see the informational messages too, set the logger's level:
logging.basicConfig()
logging.getLogger("kroger_api").setLevel(logging.INFO)
Check user input early
The same checks the client runs before a request are available in kroger_api.validation. Use them to reject bad input, such as a ZIP code typed into a form, before your code reaches the API call.
from kroger_api import InvalidParameterError, validation
try:
zip_code = validation.zip_code(user_input)
except InvalidParameterError as e:
print(f"The ZIP code {e.requirement}.")
For "45202-1234", this prints The ZIP code must be a 5-digit ZIP code (ZIP+4 is not accepted). The parameter checks table lists the other checks and their constants.
Treat empty results as possible mistakes
Some mistakes return an empty result instead of an error, so check for them when a result is empty:
- A product search with a
location_idthat does not exist returns no products. Confirm the store withkroger.location.location_exists(location_id). - A product search by
product_idreturnsmeta.warnings, such asThe following productIds were not found: 9999999999999, for IDs it does not know.
Keep older error handling working
Code written for earlier versions still catches every error response from the Kroger API, because the HTTP and network errors also derive from the requests exceptions:
import requests
try:
kroger.location.get_location("99999999")
except requests.exceptions.HTTPError as e:
print(e.response.status_code) # 404
Change except requests.exceptions.HTTPError to except APIStatusError when you want the parsed details, such as kroger_message.
An argument that fails a parameter check is not an HTTPError. Earlier versions sent it and received a 400; now the client sends nothing and raises InvalidParameterError, which derives from ValueError. To keep handling both in one place, catch KrogerAPIError.