The nekuda SDK attempts to surface actionable errors – no gigantic stack-traces, no generic Exception catch-alls. Everything inherits from NekudaError so you can decide how granular you want your try/except blocks to be.

Quick Example

from nekuda import NekudaClient, NekudaError, CardNotFoundError, NekudaValidationError

client = NekudaClient.from_env()
user_id = "user_example_456"
user = client.user(user_id)

# Assume mandate_id is obtained after user.create_mandate(...)
mandate_id_example = "mand_0abc123def456"

try:
    # This call requires a valid mandate_id obtained from user.create_mandate()
    response = user.request_card_reveal_token(mandate_id=mandate_id_example)
    print(f"Token: {response.reveal_token}")

except CardNotFoundError as exc:
    # Specific error if the card associated with user_id (and potentially mandate_id) is not found
    print(f"Card not found for user {exc.user_id}. Message: {exc.message}")
    # Ensure the user_id is correct and card details were collected.

except InvalidRequestError as exc:
    # Handles 4xx errors like invalid parameters (e.g., malformed mandate_id)
    print(f"Invalid request: {exc.message} (Code: {exc.code}, Status: {exc.status_code})")

except NekudaValidationError as exc:
    # If API response doesn't match expected Pydantic model format, or input validation fails
    print(f"Validation Error: {exc}")

except AuthenticationError as exc:
    print(f"Authentication failed: {exc.message}. Check your NEKUDA_API_KEY.")

except NekudaApiError as exc:
    # Catch other API errors (5xx, rate limits if not retried, etc.)
    print(f"nekuda API Error: {exc.message} (Code: {exc.code}, Status: {exc.status_code})")

except NekudaError as exc:
    # Catch-all for other SDK-specific errors (e.g., NekudaConnectionError)
    print(f"A nekuda SDK error occurred: {exc}")

Exception Hierarchy

All exceptions in the nekuda SDK inherit from NekudaError.

NekudaError                      # Base class for all SDK errors
 ├─ NekudaApiError               # Errors originating from the nekuda API (HTTP response received)
 │   ├─ AuthenticationError      # 401 – Missing or bad API key (NEKUDA_API_KEY)
 │   ├─ InvalidRequestError      # 4xx (e.g., 400, 404) – Malformed params, resource not found, etc.
 │   │   └─ CardNotFoundError    # Specific 404 when card details for a user are not found
 │   ├─ RateLimitError           # 429 – API rate limit exceeded (SDK retries these automatically based on max_retries)
 │   └─ ServerError              # 5xx – nekuda backend experienced an issue (SDK retries these automatically)
 ├─ NekudaConnectionError        # Network issues (DNS, TCP, TLS failures before API response)
 └─ NekudaValidationError        # Client-side Pydantic model validation failed (input or response data)

New Error Features 🚀

Retriable Errors

RateLimitError (429) and ServerError (5xx) are automatically retried by the NekudaClient based on the max_retries and backoff_factor configuration. If all retries fail, the final error is raised.

Best Practices

1. Catch Specific Errors First, Then Broader Ones

This allows for tailored error handling logic.

from nekuda import NekudaClient, NekudaError, CardNotFoundError, InvalidRequestError, AuthenticationError, NekudaValidationError

client = NekudaClient.from_env()
user = client.user("test_user")

try:
    # Your SDK calls, e.g., user.create_mandate(...), user.request_card_reveal_token(...)
    pass # Replace with actual calls
except CardNotFoundError as e:
    # Handle card not found: prompt user to add/update payment method
    print(f"Card not found: {e.message}")
except InvalidRequestError as e:
    # Handle bad request: check parameters, log error
    print(f"Invalid request to nekuda API: {e.message}, Code: {e.code}")
except AuthenticationError as e:
    # Handle auth failure: verify API key, alert ops
    print(f"nekuda API Key is invalid or missing: {e.message}")
except NekudaValidationError as e:
    # Handle validation issue (likely SDK or API contract mismatch): log, alert devs
    print(f"Data validation error: {e}")
except NekudaApiError as e:
    # Handle other API errors (e.g., rate limits after retries, server errors after retries)
    print(f"nekuda API Error: {e.message}, Status: {e.status_code}, Code: {e.code}")
except NekudaConnectionError as e:
    # Handle network issues: check connectivity, maybe retry later if appropriate for your app
    print(f"Could not connect to nekuda API: {e}")
except NekudaError as e: # Fallback for any other nekuda SDK specific errors
    print(f"An unexpected nekuda SDK error occurred: {e}")
except Exception as e: # General fallback for non-SDK errors
    print(f"An unexpected general error occurred: {e}")

2. Use Exception Attributes for Logging and Control Flow

All NekudaApiError instances (and its children like InvalidRequestError, AuthenticationError) have status_code, code, and message attributes.

# import logging_library as logger # Fictional logging library

# try:
#    # ... SDK call ...
# except NekudaApiError as e:
#     logger.error(
#         "nekuda API call failed",
#         status_code=e.status_code,
#         error_code=e.code,
#         error_message=e.message,
#         user_id=current_user_id # Add your own contextual info
#     )
#     if e.status_code == 400 and e.code == "invalid_parameter_foo":
#         # Specific handling for a known bad parameter
#         return "Please correct parameter foo."

3. Don’t Retry NekudaValidationError or AuthenticationError

  • NekudaValidationError: Indicates an issue with the data sent or received, or a mismatch with the API schema. Retrying the same call will likely fail again. This usually points to a bug in your client code or an unexpected API change.
  • AuthenticationError: Your API key is invalid. Retrying won’t help until the key is fixed.

Client-side NekudaValidationError on input (e.g., when creating MandateData) should be caught and fixed before making an API call. NekudaValidationError on response indicates an issue with the data returned by the API not matching the SDK’s expectations.

Error Response Examples

These show how attributes of exceptions can be accessed.

If nekuda API returns a JSON error like: {"error": {"code": "invalid_mandate_id", "message": "Mandate ID 'm-123' not found.", "status_code": 404}}

# try:
#     # user.request_card_reveal_token(mandate_id="m-123")
# except InvalidRequestError as e: # Or NekudaApiError for broader catch
#     print(e.code)         # "invalid_mandate_id"
#     print(e.message)      # "Mandate ID 'm-123' not found."
#     print(e.status_code)  # 404

Logging & Monitoring

Every NekudaError (and its children) can be easily logged. NekudaApiError instances also provide code and status_code.

AttributeExample (NekudaApiError)Description
message”Card details not found…”Human-readable explanation
codecard_not_foundStable machine token from API (if available)
status_code404HTTP status returned by backend (if API error)

This structure makes it trivial to ship structured logs to your observability stack:

import logging # Python's built-in logging
# import structlog # Or a structured logging library like structlog

logger = logging.getLogger(__name__)

# try:
#     # ... Your SDK call ...
# except NekudaError as e:
#     log_data = {
#         "error_type": type(e).__name__,
#         "message": str(e), # Use str(e) for the full message
#     }
#     if hasattr(e, 'code') and e.code:
#         log_data["error_code"] = e.code
#     if hasattr(e, 'status_code') and e.status_code:
#         log_data["http_status_code"] = e.status_code
#
#     logger.error("nekuda SDK operation failed", extra=log_data)

Common Scenarios & Solutions

Handle Missing Cards Gracefully (CardNotFoundError)

# def get_user_card_for_checkout(user_id: str, mandate_id: str):
#     user = client.user(user_id)
#     try:
#         token_response = user.request_card_reveal_token(mandate_id)
#         card_details = user.reveal_card_details(token_response.reveal_token)
#         return card_details # Proceed to checkout
#     except CardNotFoundError:
#         # Card not found, guide user to add/update payment method in your frontend
#         # This might involve redirecting them or showing a message.
#         print(f"No card on file for user {user_id} for this transaction.")
#         return None # Or raise a custom application error
#     except NekudaError as e:
#         print(f"Could not retrieve card: {e}")
#         return None # Or raise

Retry Logic for Flaky Networks (NekudaConnectionError, ServerError)

The nekuda SDK handles retries for ServerError and RateLimitError automatically. You typically only need custom retry logic for NekudaConnectionError if you want more control than simple immediate retries, or if you want to retry application-level logic that includes SDK calls.

# import time
# from nekuda import NekudaConnectionError, ServerError

# MAX_APP_RETRIES = 3
# for attempt in range(MAX_APP_RETRIES):
#     try:
#         # Your business logic that includes one or more SDK calls
#         # result = make_payment_attempt_with_nekuda_sdk(...)
#         # if result.is_success(): break
#     except NekudaConnectionError as e:
#         if attempt == MAX_APP_RETRIES - 1:
#             # Log final failure and give up or alert
#             raise
#         print(f"Connection error (attempt {attempt + 1}), retrying in {2 ** attempt}s...")
#         time.sleep(2 ** attempt)  # Exponential backoff for connection errors
#     except ServerError as e: # SDK already retried this, this is the final one
#          print(f"nekuda server error after SDK retries: {e}. Might need to investigate.")
#          # Depending on the error, might break or schedule for later.
#          break

Validate Mandate Data Before Sending (NekudaValidationError on input)

# from nekuda import MandateData, NekudaValidationError
#
# try:
#     # Example: Price is negative, which is invalid
#     mandate = MandateData(product="Test Item", price=-10.00, currency="USD", merchant="Test Store")
# except NekudaValidationError as e:
#     print(f"Invalid mandate data: {e}")
#     # Correct the data based on the error before attempting user.create_mandate(mandate)

Error Testing

Test your application’s error handling by simulating these scenarios with the nekuda SDK:

What’s Next?