Exceptions

All exceptions inherit from RestClientError.

Exception Hierarchy

RestClientError (base)
├── HttpError              # HTTP response errors
├── AuthenticationError    # Auth failures
│   └── TokenExpiredError  # Token expiration
├── RequestError           # Network errors
├── ResponseError          # Response parsing errors
├── RetryExhaustedError    # Retries exhausted
└── CircuitBreakerOpenError # Circuit breaker open

RestClientError

Base exception for all client errors.

from intapp_rest_client import RestClientError
 
try:
    data = client.get("/api/resources")
except RestClientError as e:
    print(f"Client error: {e}")

HttpError

Raised for HTTP response errors (4xx and 5xx status codes).

from intapp_rest_client import HttpError
 
try:
    data = client.get("/api/resources")
except HttpError as e:
    print(f"Status: {e.status_code}")
    print(f"Message: {e.message}")
    print(f"Body: {e.response_body}")
    print(f"Headers: {e.headers}")
    print(f"Request ID: {e.request_id}")

Attributes

AttributeTypeDescription
status_codeintHTTP status code
messagestrError message
response_bodyAnyResponse body (dict or str)
headersdictResponse headers
request_idstrRequest tracking ID

Properties

PropertyTypeDescription
retry_afterint | NoneSeconds from Retry-After header, if present
is_rate_limitedboolTrue if status_code == 429
is_server_errorboolTrue if status_code >= 500
is_client_errorboolTrue if 400 <= status_code < 500
is_retryableboolTrue for typical transient statuses (429, 5xx)
full_response_bodyAnySame as stored body; use when logs truncate response_body in str(e)

Example: Handling Specific Errors

try:
    data = client.get("/api/resources/123")
except HttpError as e:
    if e.status_code == 404:
        return None  # Not found is OK
    elif e.status_code == 403:
        raise PermissionError("Access denied")
    elif e.is_rate_limited:
        time.sleep(e.retry_after or 60)
        return client.get("/api/resources/123")  # Retry
    else:
        raise

AuthenticationError

Raised when authentication fails.

from intapp_rest_client import AuthenticationError
 
try:
    data = client.get("/api/resources")
except AuthenticationError as e:
    print(f"Auth failed: {e}")
    # Check credentials, refresh tokens manually, etc.

TokenExpiredError

Subclass of AuthenticationError for expired tokens.

from intapp_rest_client import TokenExpiredError
 
try:
    data = client.get("/api/resources")
except TokenExpiredError as e:
    print("Token expired and could not be refreshed")
    # Re-authenticate or get new credentials
💡

The client automatically refreshes OAuth2 tokens. TokenExpiredError is only raised when automatic refresh fails.

RequestError

Raised for network and connection errors.

from intapp_rest_client import RequestError
 
try:
    data = client.get("/api/resources")
except RequestError as e:
    print(f"Network error: {e}")
    print(f"Request ID: {e.request_id}")

Common Causes

  • DNS resolution failed
  • Connection refused
  • Connection timeout
  • SSL/TLS certificate errors
  • Network unreachable

Attributes

AttributeTypeDescription
request_idstrRequest tracking ID (if generated)

ResponseError

Raised when response parsing fails.

from intapp_rest_client import ResponseError
 
try:
    data = client.get("/api/resources")
except ResponseError as e:
    print(f"Could not parse response: {e}")

Common Causes

  • Invalid JSON in response
  • Unexpected response format
  • Encoding issues

RetryExhaustedError

Raised when all retry attempts have been exhausted (from the internal retry transport).

from intapp_rest_client import RetryExhaustedError
 
try:
    data = client.get("/api/resources")
except RetryExhaustedError as e:
    print(e)  # Message includes retry count and last status when available
    if e.last_error is not None:
        print(f"Underlying error: {e.last_error}")

Attributes

AttributeTypeDescription
last_errorException | NoneLast underlying exception from the transport (e.g. network error), if any
request_idstr | NoneRequest ID when available

The exception message string summarizes failures; there are no separate attempts or last_status_code attributes.

CircuitBreakerOpenError

Raised when a RetryTransport or AsyncRetryTransport is configured with a CircuitBreaker and the breaker is open (fail-fast). The default RestClient stack does not attach a circuit breaker, so you will not see this from typical RestClient usage unless you build custom transports.

from intapp_rest_client import CircuitBreakerOpenError
 
try:
    ...
except CircuitBreakerOpenError as e:
    print("Circuit breaker is open")
    if e.reset_time is not None:
        print(f"Suggested wait before retry: {e.reset_time}s")

Attributes

AttributeTypeDescription
reset_timefloat | NoneOptional hint (seconds) when the breaker may allow a probe
request_idstr | NoneRequest ID when available

Import All Exceptions

from intapp_rest_client import (
    RestClientError,
    HttpError,
    AuthenticationError,
    TokenExpiredError,
    RequestError,
    ResponseError,
    RetryExhaustedError,
    CircuitBreakerOpenError
)

Best Practices

1. Catch Specific Exceptions

# ✅ Good - handle specific cases
try:
    data = client.get("/api/resources")
except HttpError as e:
    if e.status_code == 404:
        return None
    raise
except RequestError:
    logger.error("Network issue, will retry later")
    raise
 
# ❌ Avoid - too broad
try:
    data = client.get("/api/resources")
except Exception:
    pass  # Hides real problems

2. Log Request IDs

try:
    data = client.get("/api/resources")
except HttpError as e:
    logger.error(f"Request {e.request_id} failed: {e.status_code}")
    raise

3. Handle Rate Limits Gracefully

try:
    data = client.get("/api/resources")
except HttpError as e:
    if e.is_rate_limited:
        wait = e.retry_after or 60
        logger.warning(f"Rate limited, waiting {wait}s")
        time.sleep(wait)
        return client.get("/api/resources")
    raise