Resiliant Connections

Patterns for building resilient API clients and real-time connections with retry logic, circuit breakers, and graceful degradation. Use when building production systems that need to handle failures. Triggers on retry logic, circuit breaker, connection resilience, exponential backoff, API client, fault tolerance.

MIT-0 · Free to use, modify, and redistribute. No attribution required.
2 · 734 · 3 current installs · 3 all-time installs
MIT-0
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
Name and description match the included content: TypeScript patterns for retries, circuit breakers, resilient fetch, reconnecting WebSocket, and graceful degradation. There are no extraneous credential or binary requirements.
Instruction Scope
SKILL.md contains code samples and guidance focused solely on building resilient clients and connections. It does not instruct the agent to read system files, environment secrets, or send data to any external endpoint. Installation notes reference local copy commands and an npx-based install; these are procedural and not data-collection steps.
Install Mechanism
There is no install spec (instruction-only), so nothing is downloaded or executed automatically by the platform. README suggests manual copying or an npx install from a GitHub path — this is not executed by the skill itself, and you should treat any external npx/GitHub install command as you would any third-party package (review before running).
Credentials
The skill declares no required environment variables, credentials, or config paths. The code samples do not reference secrets or unrelated environment variables.
Persistence & Privilege
Flags are default (always:false, user-invocable:true). The skill does not request persistent platform privileges or attempt to modify other skills or system-wide configuration in its instructions.
Assessment
This skill is a set of code examples and guidance for resilience patterns and appears internally consistent. Before installing or copying into a project: (1) review the snippets and any code you actually paste into your codebase, (2) avoid running npx or remote install commands from untrusted URLs without inspection, and (3) when adapting to your app, validate dependency choices and ensure non-idempotent requests aren’t retried. The skill does not request credentials or attempt to access system files, but always inspect third-party code before adding it to production.

Like a lobster shell, security has layers — review code before you run it.

Current versionv1.0.0
Download zip
latestvk9756ggvxr7vwwnwn9pytys7g980w1wy

License

MIT-0
Free to use, modify, and redistribute. No attribution required.

SKILL.md

Resilient Connections

Build API clients and real-time connections that handle failures gracefully with retries, circuit breakers, and fallbacks.

Installation

OpenClaw / Moltbot / Clawbot

npx clawhub@latest install resilient-connections

When to Use

  • Building API clients that need to handle transient failures
  • Real-time connections that should reconnect automatically
  • Systems that need graceful degradation
  • Any production system calling external services

Pattern 1: Exponential Backoff

interface RetryOptions {
  maxRetries: number;
  baseDelay: number;
  maxDelay: number;
  jitter?: boolean;
}

async function withRetry<T>(
  fn: () => Promise<T>,
  options: RetryOptions
): Promise<T> {
  const { maxRetries, baseDelay, maxDelay, jitter = true } = options;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries) throw error;

      // Calculate delay with exponential backoff
      let delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
      
      // Add jitter to prevent thundering herd
      if (jitter) {
        delay = delay * (0.5 + Math.random());
      }

      await sleep(delay);
    }
  }

  throw new Error('Unreachable');
}

// Usage
const data = await withRetry(
  () => fetch('/api/data').then(r => r.json()),
  { maxRetries: 3, baseDelay: 1000, maxDelay: 30000 }
);

Pattern 2: Circuit Breaker

enum CircuitState {
  Closed,    // Normal operation
  Open,      // Failing, reject requests
  HalfOpen,  // Testing if recovered
}

class CircuitBreaker {
  private state = CircuitState.Closed;
  private failures = 0;
  private lastFailure = 0;
  
  constructor(
    private threshold: number = 5,
    private timeout: number = 30000
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === CircuitState.Open) {
      if (Date.now() - this.lastFailure > this.timeout) {
        this.state = CircuitState.HalfOpen;
      } else {
        throw new Error('Circuit breaker is open');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess() {
    this.failures = 0;
    this.state = CircuitState.Closed;
  }

  private onFailure() {
    this.failures++;
    this.lastFailure = Date.now();
    
    if (this.failures >= this.threshold) {
      this.state = CircuitState.Open;
    }
  }
}

Pattern 3: Resilient Fetch Wrapper

interface FetchOptions extends RequestInit {
  timeout?: number;
  retries?: number;
}

async function resilientFetch(
  url: string,
  options: FetchOptions = {}
): Promise<Response> {
  const { timeout = 10000, retries = 3, ...fetchOptions } = options;

  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  const fetchWithTimeout = async () => {
    try {
      const response = await fetch(url, {
        ...fetchOptions,
        signal: controller.signal,
      });

      if (!response.ok && response.status >= 500) {
        throw new Error(`Server error: ${response.status}`);
      }

      return response;
    } finally {
      clearTimeout(timeoutId);
    }
  };

  return withRetry(fetchWithTimeout, {
    maxRetries: retries,
    baseDelay: 1000,
    maxDelay: 10000,
  });
}

Pattern 4: Reconnecting WebSocket

class ReconnectingWebSocket {
  private ws: WebSocket | null = null;
  private retries = 0;
  private maxRetries = 10;

  constructor(
    private url: string,
    private onMessage: (data: unknown) => void
  ) {
    this.connect();
  }

  private connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      this.retries = 0;
    };

    this.ws.onmessage = (event) => {
      this.onMessage(JSON.parse(event.data));
    };

    this.ws.onclose = () => {
      if (this.retries < this.maxRetries) {
        const delay = Math.min(1000 * 2 ** this.retries, 30000);
        this.retries++;
        setTimeout(() => this.connect(), delay);
      }
    };
  }

  send(data: unknown) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }

  close() {
    this.maxRetries = 0; // Prevent reconnection
    this.ws?.close();
  }
}

Pattern 5: Graceful Degradation

async function fetchWithFallback<T>(
  primary: () => Promise<T>,
  fallback: () => Promise<T>,
  cache?: T
): Promise<T> {
  try {
    return await primary();
  } catch (primaryError) {
    console.warn('Primary failed, trying fallback:', primaryError);
    
    try {
      return await fallback();
    } catch (fallbackError) {
      console.warn('Fallback failed:', fallbackError);
      
      if (cache !== undefined) {
        console.warn('Using cached data');
        return cache;
      }
      
      throw fallbackError;
    }
  }
}

// Usage
const data = await fetchWithFallback(
  () => fetchFromPrimaryAPI(),
  () => fetchFromBackupAPI(),
  cachedData
);

Related Skills


NEVER Do

  • NEVER retry non-idempotent requests — POST/PUT might succeed but fail to respond
  • NEVER use fixed delays — Always add jitter to prevent thundering herd
  • NEVER retry 4xx errors — Client errors won't resolve themselves
  • NEVER keep circuit open forever — Always have a timeout to half-open
  • NEVER hide connection failures — Show users the degraded state

Quick Reference

// Exponential backoff
const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);

// With jitter
const jitteredDelay = delay * (0.5 + Math.random());

// Retry check
const shouldRetry = 
  error.status >= 500 || 
  error.code === 'ETIMEDOUT' ||
  error.code === 'ECONNRESET';

// Circuit breaker states
Closed -> (failures >= threshold) -> Open
Open -> (timeout elapsed) -> HalfOpen
HalfOpen -> (success) -> Closed
HalfOpen -> (failure) -> Open

Files

2 total
Select a file
Select a file to preview.

Comments

Loading comments…