Error Handling
Error Types
All SDKs map HTTP status codes to specific typed error classes. Every error class inherits from a base error type for catch-all handling.
| HTTP Status | Error Code | Python | TypeScript | Go |
|---|---|---|---|---|
| 400 | BAD_REQUEST | BadRequestError | BadRequestError | *BadRequestError |
| 401 | AUTHENTICATION_ERROR | AuthenticationError | AuthenticationError | *UnauthorizedError |
| 403 | AUTHORIZATION_ERROR | AuthorizationError | AuthorizationError | *ForbiddenError |
| 404 | NOT_FOUND | NotFoundError | NotFoundError | *NotFoundError |
| 409 | CONFLICT | ConflictError | ConflictError | *ConflictError |
| 422 | VALIDATION_ERROR | ValidationError | ValidationError | *UnprocessableEntityError |
| 429 | RATE_LIMIT_EXCEEDED | RateLimitError | RateLimitError | *TooManyRequestsError |
| 500 | INTERNAL_SERVER_ERROR | InternalServerError | InternalServerError | *InternalServerError |
Base error classes:
- Python:
LevelFourError - TypeScript:
LevelFourError - Go:
*core.APIError(useerrors.Asto match specific types)
Additional non-HTTP errors:
- Python:
LevelFourConnectionError,LevelFourTimeoutError - TypeScript:
LevelFourConnectionError,LevelFourTimeoutError
Catching Errors
Python
from levelfour import (
LevelFour,
LevelFourError,
AuthenticationError,
NotFoundError,
RateLimitError,
ValidationError,
)
client = LevelFour()
try:
detail = client.recommendations.get("rec_123")
except NotFoundError as e:
print(f"Not found: {e.message}")
print(f"Status: {e.status_code}")
print(f"Code: {e.code}")
except RateLimitError as e:
print(f"Rate limited. Retry after: {e.retry_after}")
except ValidationError as e:
print(f"Validation errors: {e.details}")
except AuthenticationError as e:
print(f"Auth failed: {e.message}")
except LevelFourError as e:
print(f"API error {e.status_code}: {e.message}")TypeScript
import {
LevelFourClient,
LevelFourError,
NotFoundError,
RateLimitError,
ValidationError,
AuthenticationError,
} from "levelfour";
const client = new LevelFourClient();
try {
await client.recommendations.get({ recommendation_id: "rec_123" });
} catch (err) {
if (err instanceof NotFoundError) {
console.log("Not found:", err.message);
console.log("Status:", err.statusCode);
} else if (err instanceof RateLimitError) {
console.log("Rate limited");
} else if (err instanceof ValidationError) {
console.log("Validation:", err.body);
} else if (err instanceof AuthenticationError) {
console.log("Auth failed:", err.message);
} else if (err instanceof LevelFourError) {
console.log(`API error ${err.statusCode}: ${err.message}`);
}
}Go
import (
"errors"
"fmt"
"github.com/LevelFourAI/levelfour-go/levelfour"
)
detail, err := client.Recommendations.Get(ctx, "rec_123")
if err != nil {
var notFoundErr *levelfour.NotFoundError
var rateLimitErr *levelfour.TooManyRequestsError
var badReqErr *levelfour.BadRequestError
var unprocessableErr *levelfour.UnprocessableEntityError
switch {
case errors.As(err, ¬FoundErr):
fmt.Printf("Not found: %v\n", notFoundErr.Body)
case errors.As(err, &rateLimitErr):
fmt.Printf("Rate limited: %v\n", rateLimitErr.Body)
case errors.As(err, &badReqErr):
fmt.Printf("Bad request: %v\n", badReqErr.Body)
case errors.As(err, &unprocessableErr):
fmt.Printf("Validation: %v\n", unprocessableErr.Body)
default:
fmt.Printf("Error: %v\n", err)
}
}Error Properties
Python
All exceptions inherit from LevelFourError:
| Property | Type | Description |
|---|---|---|
status_code | int | HTTP status code (0 for connection/timeout errors) |
code | str | Error code string |
message | str | Human-readable error message |
details | dict | list | None | Additional error context |
RateLimitError adds:
| Property | Type | Description |
|---|---|---|
retry_after | str | None | Seconds until retry is allowed |
TypeScript
All error classes extend LevelFourError:
| Property | Type | Description |
|---|---|---|
statusCode | number | undefined | HTTP status code |
body | unknown | Parsed error response body |
rawResponse | RawResponse | undefined | Raw HTTP response |
message | string | Error message |
Go
All error types embed *core.APIError:
| Property | Type | Description |
|---|---|---|
StatusCode | int | HTTP status code |
Body | typed | Parsed error response (type varies per error) |
The Body field is typed per error: BadRequest, AuthenticationError, AuthorizationError, NotFound, Conflict, *HTTPValidationError, RateLimitError, InternalServer.
Retry Behavior
All SDKs automatically retry failed requests with exponential backoff. The default is 2 retries.
Retryable Errors
| Condition | Retried |
|---|---|
| Network errors / timeouts | Yes |
| 408 Request Timeout | Yes |
| 409 Conflict | Yes |
| 429 Too Many Requests | Yes |
| 5xx Server Errors | Yes |
| 400 Bad Request | No |
| 401 Unauthorized | No |
| 403 Forbidden | No |
| 404 Not Found | No |
| 422 Validation Error | No |
Configuring Retries
client = LevelFour(max_retries=5)
client = LevelFour(max_retries=0)Per-Request Override
summary = client.recommendations.get_savings_by_provider(
request_options={"max_retries": 5},
)