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 StatusError CodePythonTypeScriptGo
400BAD_REQUESTBadRequestErrorBadRequestError*BadRequestError
401AUTHENTICATION_ERRORAuthenticationErrorAuthenticationError*UnauthorizedError
403AUTHORIZATION_ERRORAuthorizationErrorAuthorizationError*ForbiddenError
404NOT_FOUNDNotFoundErrorNotFoundError*NotFoundError
409CONFLICTConflictErrorConflictError*ConflictError
422VALIDATION_ERRORValidationErrorValidationError*UnprocessableEntityError
429RATE_LIMIT_EXCEEDEDRateLimitErrorRateLimitError*TooManyRequestsError
500INTERNAL_SERVER_ERRORInternalServerErrorInternalServerError*InternalServerError

Base error classes:

  • Python: LevelFourError
  • TypeScript: LevelFourError
  • Go: *core.APIError (use errors.As to 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, &notFoundErr):
        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:

PropertyTypeDescription
status_codeintHTTP status code (0 for connection/timeout errors)
codestrError code string
messagestrHuman-readable error message
detailsdict | list | NoneAdditional error context

RateLimitError adds:

PropertyTypeDescription
retry_afterstr | NoneSeconds until retry is allowed

TypeScript

All error classes extend LevelFourError:

PropertyTypeDescription
statusCodenumber | undefinedHTTP status code
bodyunknownParsed error response body
rawResponseRawResponse | undefinedRaw HTTP response
messagestringError message

Go

All error types embed *core.APIError:

PropertyTypeDescription
StatusCodeintHTTP status code
BodytypedParsed 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

ConditionRetried
Network errors / timeoutsYes
408 Request TimeoutYes
409 ConflictYes
429 Too Many RequestsYes
5xx Server ErrorsYes
400 Bad RequestNo
401 UnauthorizedNo
403 ForbiddenNo
404 Not FoundNo
422 Validation ErrorNo

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},
)