Skip to content

Retry Strategies

Both SDKs include built-in retry logic for transient failures. This guide explains how retries work and how to customize them.

Built-in Behavior

Setting Default Description
Max retries 3 Total retry attempts after the initial request
Backoff Exponential 0.5s → 1s → 2s (Python), 500ms → 1s → 2s (TypeScript)
Retried errors 5xx only Server errors and connection/timeout failures
Not retried 4xx Client errors (auth, validation, rate limit) are never retried

How It Works

Request 1 → 503 → wait 0.5s
Request 2 → 503 → wait 1.0s
Request 3 → 503 → wait 2.0s
Request 4 → 503 → raise ServerError

If any attempt succeeds, the response is returned immediately.

Customizing Retries

Disable Retries

client = CloMan(api_key="cloman_...", max_retries=0)
const client = new CloMan("cloman_...", { maxRetries: 0 });

Increase Retries

client = CloMan(api_key="cloman_...", max_retries=5)
const client = new CloMan("cloman_...", { maxRetries: 5 });

Custom Retry Logic

For more control, disable built-in retries and implement your own:

import time
from cloman import CloMan
from cloman._exceptions import ServerError, RateLimitError

client = CloMan(api_key="cloman_...", max_retries=0)

def decide_with_retry(context: str, max_attempts: int = 3) -> ...:
    for attempt in range(max_attempts):
        try:
            return client.decide(context=context)
        except ServerError:
            if attempt < max_attempts - 1:
                time.sleep(2 ** attempt)
                continue
            raise
        except RateLimitError as e:
            if e.retry_after and attempt < max_attempts - 1:
                time.sleep(e.retry_after)
                continue
            raise
import { CloMan, ServerError, RateLimitError } from "@clomanai/sdk";

const client = new CloMan("cloman_...", { maxRetries: 0 });

async function decideWithRetry(context: string, maxAttempts = 3) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await client.decide({ context });
    } catch (error) {
      if (error instanceof ServerError && attempt < maxAttempts - 1) {
        await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
        continue;
      }
      if (error instanceof RateLimitError && error.retryAfter && attempt < maxAttempts - 1) {
        await new Promise((r) => setTimeout(r, error.retryAfter! * 1000));
        continue;
      }
      throw error;
    }
  }
}

What Gets Retried

Scenario Retried? Reason
500 Internal Server Error Yes Transient server issue
502 Bad Gateway Yes Upstream service issue
503 Service Unavailable Yes Temporary overload
Connection refused Yes Server might be restarting
Request timeout Yes Network hiccup
401 Authentication Error No Invalid key won't become valid
403 Authorization Error No Missing permissions won't appear
404 Not Found No Resource won't appear
422 Validation Error No Bad input won't fix itself
429 Rate Limit No Handled separately via retry_after