Error Handling

The library translates gRPC status codes into a clean exception hierarchy rooted at DnaerysError. All exceptions expose an is_retryable property to help with retry logic.

Exception hierarchy

DnaerysError (base)                       is_retryable = False
+-- DnaerysConnectionError                is_retryable = True
|       UNAVAILABLE, DEADLINE_EXCEEDED
+-- DnaerysAuthenticationError             is_retryable = False
|       UNAUTHENTICATED, PERMISSION_DENIED
+-- DnaerysNotFoundError                   is_retryable = False
|       NOT_FOUND
+-- DnaerysInvalidRequestError             is_retryable = False
|       INVALID_ARGUMENT, FAILED_PRECONDITION, OUT_OF_RANGE
+-- DnaerysServerError                     is_retryable = False
|       INTERNAL, UNKNOWN, DATA_LOSS, UNIMPLEMENTED
+-- DnaerysResourceExhausted               is_retryable = True
        RESOURCE_EXHAUSTED

DnaerysIncompleteResultWarning (UserWarning — not part of error hierarchy)
    Emitted when affected=True in a server response.

Catching errors

Catch the base class to handle any Dnaerys error:

from dnaerys import DnaerysClient, DnaerysError

try:
    with DnaerysClient("db.dnaerys.org:443") as client:
        result = client.health()
except DnaerysError as e:
    print(f"Error: {e}")
    print(f"Retryable: {e.is_retryable}")

Or catch specific subclasses for targeted handling:

from dnaerys import (
    DnaerysClient, Region,
    DnaerysConnectionError,
    DnaerysInvalidRequestError,
    DnaerysError,
)

try:
    with DnaerysClient("db.dnaerys.org:443") as client:
        for v in client.select_variants():
            print(v)
except DnaerysInvalidRequestError as e:
    print(f"Bad request (retryable={e.is_retryable}): {e}")
except DnaerysConnectionError as e:
    print(f"Connection failed (retryable={e.is_retryable}): {e}")
except DnaerysError as e:
    print(f"Other error: {e}")

Client-side validation

Input types validate their parameters at construction time. These raise standard ValueError or TypeError, not DnaerysError:

from dnaerys import Region

try:
    region = Region("chr17", -1, 7687546)
except ValueError as e:
    print(f"ValueError: {e}")
    # "start must be >= 1, got -1"

Retry logic

Use the is_retryable property to implement retry logic:

import time
from dnaerys import DnaerysClient, Region, DnaerysError

def query_with_retry(client, region, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.count_variants(region=region)
        except DnaerysError as e:
            if not e.is_retryable or attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)  # Exponential backoff

Currently retryable exceptions:

Incomplete result warning

DnaerysIncompleteResultWarning is a UserWarning (not part of the error hierarchy) emitted when results may be incomplete due to unreachable cluster nodes. It fires when a server response has affected=True.

Suppress it if needed:

import warnings
from dnaerys import DnaerysIncompleteResultWarning

warnings.filterwarnings(
    "ignore", category=DnaerysIncompleteResultWarning,
)

Accessing the original gRPC error

The original gRPC error is chained via raise ... from, so you can inspect the raw status code and details:

from dnaerys import DnaerysClient, DnaerysError

try:
    with DnaerysClient("db.dnaerys.org:443") as client:
        client.health()
except DnaerysError as e:
    if e.__cause__:
        grpc_error = e.__cause__
        print(f"gRPC code: {grpc_error.code()}")
        print(f"gRPC details: {grpc_error.details()}")