Skip to content

By the Octri team · Updated

SDK error handling: how to design errors developers can use

Errors are part of an SDK's public interface. Developers often meet them only after a system has been deployed, when a rate limit, expired credential or timeout interrupts the happy path. At that point, the exception needs to convey enough structure to recover without forcing the caller to parse prose or consult raw HTTP logs.

How should an SDK handle errors?

Build one consistent hierarchy. Separate validation errors raised before a request, connection failures where no response arrived, and HTTP status errors where the server answered. Keep status, headers, a request ID and a parsed body where available. Expose retry timing and preserve the original transport exception as a cause.

The objective is a caller that can write a small except or catch block and make the right decision from fields, not from guesses about a message string.

The three kinds of failure

A malformed argument caught locally is different from a 422 returned by the service; the former never left the process. A DNS or connection timeout may have occurred before or after the server acted, so the result of a write can be unknown. A status error confirms that an HTTP response arrived, even if it was an error response.

That distinction matters for retries. Repeating a POST after an ambiguous connection failure can produce duplicate work unless the operation and API support idempotency.

Build an error hierarchy

A small Python design might have AcmeError at the root, APIConnectionError for transport failures and APIStatusError for non-successful responses. Under APIStatusError, provide common subclasses such as NotFoundError, RateLimitError and InternalServerError.

python
class AcmeError(Exception):
    pass

class APIConnectionError(AcmeError):
    pass

class APIStatusError(AcmeError):
    def __init__(self, status, request_id, body):
        self.status = status
        self.request_id = request_id
        self.body = body
        super().__init__(f'HTTP {status} (request {request_id})')

class NotFoundError(APIStatusError):
    pass

Keep the hierarchy predictable across every method. A 404 should not be a special string on one resource and a typed exception on another.

Diagram of SDK validation errors, connection errors and HTTP status error subclasses
Three distinct failure categories let callers respond without guessing.

What every error should carry

I sent a request for a missing order to a local test server and checked the response. It returned 404, a structured Order not found body and X-Request-ID: req_test_17. The useful part was not the English message; it was that the caller could distinguish a missing resource and hand support an identifier that points to the server's request record. The test did not involve customer data or a production incident.

Capture those fields in the exception, along with retry-relevant headers where present. Do not serialize sensitive request headers into logs or exception messages just because a debugger might find them convenient.

Parse error bodies as problem details

RFC 9457 defines an HTTP problem-details shape with fields including type, title, status, detail and instance. When a service uses it, preserve those values instead of flattening them into one sentence.

json
{
  "type": "https://api.example.test/problems/not-found",
  "title": "Order not found",
  "status": 404,
  "detail": "No order matches this identifier"
}

Handle HTML errors from proxies and empty bodies gracefully. A parsing failure must not hide the original HTTP status.

DigitalOcean API reference documenting non-success status bodies in an unofficial Octri showcase
An explicit API error schema gives a generator useful structure to preserve.

What callers write

A usable client makes this possible:

python
try:
    order = client.orders.retrieve('ord_missing')
except NotFoundError as exc:
    logger.info('Order missing, request=%s', exc.request_id)
except APIConnectionError:
    # The server may or may not have seen the request.
    raise

The caller chooses policy. The SDK supplies reliable information and reasonable defaults.

Which errors to retry

Temporary 5xx responses, some 429 responses and certain connection failures may warrant another attempt with bounded backoff. An invalid argument, 401 with bad credentials or a permission-denied 403 generally will not be repaired by waiting. For writes, retry only when the operation is safe or the server guarantees idempotency for the repeated key.

A rate limit should reveal the Retry-After value; an exhausted retry budget should leave the original status and request ID available.

Errors in each language

TypeScript users generally catch error classes; Python users catch exception subclasses; Go callers inspect returned errors with errors.Is or errors.As; Java callers work with exception types. Preserve the same conceptual categories while following each language's idioms.

What a generated SDK does with errors

A generator can turn declared HTTP status responses into typed variants, but only if the API description records them and the generator supports the mapping. A vague default response cannot convey every useful failure type. Keep the status mapping and problem-details parsing centralized rather than reproducing them inside individual endpoint methods.

DigitalOcean TypeScript SDK error hierarchy in an unofficial Octri documentation showcase
Generated SDKs should expose status, request ID and typed failures consistently.

Common SDK error mistakes

Avoid treating every non-200 as failure when 201 or 204 is valid, discarding response headers, retrying permanent validation errors, and revealing credentials in exception strings. Do not turn a connection timeout into a fictional 500; no response arrived, so there is no known status code.

SDK error handling checklist

Trigger an invalid local argument, 401, 404, 429, 500, an empty error response and a connection refusal. Confirm the hierarchy, fields, retry behavior and redaction. Test each case in every supported language, even when the SDKs share an OpenAPI source.

SDK error handling with Octri

Octri's SDK generator can provide common error handling and customizable request behavior across methods. Pair that with complete error responses in the OpenAPI spec so the generated client has enough information to expose useful typed failures.

Keep reading