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 openRestClientError
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
| Attribute | Type | Description |
|---|---|---|
status_code | int | HTTP status code |
message | str | Error message |
response_body | Any | Response body (dict or str) |
headers | dict | Response headers |
request_id | str | Request tracking ID |
Properties
| Property | Type | Description |
|---|---|---|
retry_after | int | None | Seconds from Retry-After header, if present |
is_rate_limited | bool | True if status_code == 429 |
is_server_error | bool | True if status_code >= 500 |
is_client_error | bool | True if 400 <= status_code < 500 |
is_retryable | bool | True for typical transient statuses (429, 5xx) |
full_response_body | Any | Same 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:
raiseAuthenticationError
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 credentialsThe 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
| Attribute | Type | Description |
|---|---|---|
request_id | str | Request 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
| Attribute | Type | Description |
|---|---|---|
last_error | Exception | None | Last underlying exception from the transport (e.g. network error), if any |
request_id | str | None | Request 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
| Attribute | Type | Description |
|---|---|---|
reset_time | float | None | Optional hint (seconds) when the breaker may allow a probe |
request_id | str | None | Request 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 problems2. Log Request IDs
try:
data = client.get("/api/resources")
except HttpError as e:
logger.error(f"Request {e.request_id} failed: {e.status_code}")
raise3. 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