Error Handling

The Replane SDK uses a hierarchy of exceptions to help you handle errors appropriately.

Exception Hierarchy

ReplaneError (base class)
├── ConfigNotFoundError (used for required configs at init time)
├── TimeoutError
├── AuthenticationError
├── NetworkError
├── ClientClosedError
├── NotInitializedError
└── MissingDependencyError

Note: Accessing a missing config via replane.configs["name"] raises a standard KeyError, not ConfigNotFoundError. Use replane.configs.get("name", default) to avoid exceptions.

Error Codes

Each ReplaneError has a code attribute from the ErrorCode enum:

Code

Description

not_found

Config doesn’t exist (required configs)

timeout

Operation timed out

network_error

Network request failed

auth_error

Authentication failed (invalid SDK key)

forbidden

Access denied

server_error

Server returned 5xx error

client_error

Client error (4xx)

closed

Client has been closed

not_initialized

Client not yet initialized

missing_dependency

Required dependency not installed

unknown

Unknown error

Handling Errors

Basic Error Handling

from replane import (
    Replane,
    ReplaneError,
    TimeoutError,
    AuthenticationError,
)

try:
    with Replane(
        base_url="https://cloud.replane.dev",
        sdk_key="rp_...",
    ) as replane:
        value = replane.configs["my_config"]
except KeyError as e:
    print(f"Config not found: {e}")
except TimeoutError as e:
    print(f"Timed out after {e.timeout_ms}ms")
except AuthenticationError:
    print("Invalid SDK key - check your configuration")
except ReplaneError as e:
    print(f"Replane error [{e.code}]: {e.message}")

Using Error Codes

from replane import ReplaneError, ErrorCode

try:
    replane.connect()
except ReplaneError as e:
    match e.code:
        case ErrorCode.TIMEOUT:
            # Maybe retry
            pass
        case ErrorCode.AUTH_ERROR:
            # Log and alert
            logger.critical("Replane authentication failed!")
        case _:
            # Generic handling
            logger.error(f"Replane error: {e}")

Specific Exceptions

KeyError (Missing Config)

Accessing a missing config via bracket notation raises a standard KeyError:

try:
    value = replane.configs["nonexistent_config"]
except KeyError as e:
    print(f"Config not found: {e}")
    value = "default"

Prevention: Use .get() method or defaults option:

# With get() method
value = replane.configs.get("config", "fallback")

# With defaults during init
replane = Replane(
    ...,
    defaults={"config": "fallback"},
)

# With with_defaults()
safe_client = replane.with_defaults({"config": "fallback"})
value = safe_client.configs["config"]  # Returns "fallback" if not configured

ConfigNotFoundError

Raised when required configs are missing during initialization.

from replane import Replane, ConfigNotFoundError

try:
    with Replane(
        ...,
        required=["critical_config-1", "critical_config-2"],
    ) as replane:
        pass
except ConfigNotFoundError as e:
    print(f"Missing required configs: {e}")

Attributes:

  • config_name: str - Name or description of missing config(s)

TimeoutError

Raised when an operation exceeds its timeout.

from replane import TimeoutError

try:
    replane.connect()
except TimeoutError as e:
    print(f"Connection timed out after {e.timeout_ms}ms")

Attributes:

  • timeout_ms: int | None - Timeout value in milliseconds

Prevention: Increase timeout values:

replane = Replane(
    ...,
    initialization_timeout_ms=10000,  # 10 seconds
    request_timeout_ms=5000,  # 5 seconds
)

AuthenticationError

Raised when the SDK key is invalid or missing.

from replane import AuthenticationError

try:
    replane.connect()
except AuthenticationError:
    print("Check your SDK key!")

Common causes:

  • Invalid SDK key

  • SDK key for wrong environment

  • Revoked SDK key

NetworkError

Raised when a network request fails.

from replane import NetworkError

try:
    replane.connect()
except NetworkError as e:
    print(f"Network error: {e.message}")
    if e.__cause__:
        print(f"Caused by: {e.__cause__}")

Common causes:

  • Server unreachable

  • DNS resolution failed

  • Connection refused

  • SSL/TLS errors

ClientClosedError

Raised when attempting operations on a closed client.

from replane import ClientClosedError

replane = Replane(...)
replane.connect()
replane.close()

try:
    _ = replane.configs["config"]  # Raises ClientClosedError
except ClientClosedError:
    print("Client was already closed")

NotInitializedError

Raised when the client hasn’t finished initializing.

from replane import NotInitializedError

replane = Replane(...)
replane.connect(wait=False)  # Don't wait

try:
    _ = replane.configs["config"]  # May raise if not ready
except NotInitializedError:
    replane.wait_for_init()  # Wait then retry
    value = replane.configs["config"]

MissingDependencyError

Raised when using features that require optional dependencies.

from replane import AsyncReplane, MissingDependencyError

try:
    replane = AsyncReplane(...)
except MissingDependencyError as e:
    print(f"Missing: {e.dependency}")
    print(f"Install with: pip install replane[async]")

Attributes:

  • dependency: str - Name of the missing package

  • feature: str - Feature that requires the dependency

Error Cause Chain

Errors preserve the original cause for debugging:

try:
    replane.connect()
except ReplaneError as e:
    print(f"Error: {e.message}")
    if e.__cause__:
        print(f"Original error: {e.__cause__}")

Best Practices

  1. Catch specific exceptions first, then fall back to ReplaneError

  2. Use defaults for resilience against missing configs

  3. Log errors with their codes for debugging

  4. Don’t catch and ignore - at minimum, log the error

  5. Use .get() method instead of catching KeyError when appropriate

# Good: specific handling
try:
    value = replane.configs["critical_config"]
except KeyError:
    logger.error("Critical config missing!")
    raise  # Re-raise for critical configs

# Good: graceful fallback
value = replane.configs.get("optional-config", "safe-default")

# Bad: silently ignoring
try:
    value = replane.configs["config"]
except KeyError:
    pass  # Don't do this!