Error Handling

civicrm-py raises specific exceptions for different error conditions.

Exception Hierarchy

All civicrm-py exceptions inherit from CiviError:

CiviError
├── CiviConfigError      # Configuration problems
├── CiviConnectionError  # Network failures
├── CiviTimeoutError     # Request timeout
├── CiviAuthError        # Authentication failed
├── CiviAPIError         # API returned an error
│   ├── CiviNotFoundError     # Record not found
│   ├── CiviValidationError   # Invalid data
│   └── CiviPermissionError   # Permission denied

Catching Exceptions

Catch specific exceptions for targeted handling:

from civicrm_py import (
    CiviClient,
    CiviConnectionError,
    CiviAuthError,
    CiviAPIError,
)

async with CiviClient() as client:
    try:
        response = await client.get("Contact")
    except CiviConnectionError:
        # Network problem, maybe retry later
        print("Cannot reach CiviCRM server")
    except CiviAuthError:
        # Bad credentials
        print("Check your API key")
    except CiviAPIError as e:
        # CiviCRM returned an error
        print(f"API error: {e}")

CiviConnectionError

Raised when the HTTP request fails:

  • Server unreachable

  • DNS resolution failed

  • Connection refused

try:
    await client.get("Contact")
except CiviConnectionError:
    # Server down or network issue
    pass

CiviTimeoutError

Raised when the request exceeds the timeout:

try:
    await client.get("Contact")
except CiviTimeoutError:
    # Request took too long
    pass

CiviAuthError

Raised when authentication fails:

  • Invalid API key

  • Invalid site key

  • Expired JWT token

try:
    await client.get("Contact")
except CiviAuthError:
    # Bad credentials
    pass

CiviAPIError

Raised when CiviCRM returns an error response. Includes the error message from the API:

try:
    await client.create("Contact", values={"invalid_field": "value"})
except CiviAPIError as e:
    print(f"Error: {e}")

CiviNotFoundError

Raised when a requested record does not exist:

try:
    await client.get("Contact", where=[["id", "=", 999999]])
except CiviNotFoundError:
    print("Contact not found")

CiviValidationError

Raised when data validation fails:

try:
    await client.create("Contact", values={})  # Missing required fields
except CiviValidationError as e:
    print(f"Invalid data: {e}")

CiviPermissionError

Raised when the API key lacks permission:

try:
    await client.delete("Contact", where=[["id", "=", 1]])
except CiviPermissionError:
    print("Not allowed to delete contacts")

Retry Behavior

The client automatically retries on transient failures. Configure retry count:

client = CiviClient(max_retries=5)

Retried errors:

  • Connection timeouts

  • 503 Service Unavailable

  • 502 Bad Gateway

  • 504 Gateway Timeout

Not retried:

  • Authentication errors

  • Validation errors

  • Permission errors

  • 4xx client errors