WebSocket Error Handling

Comprehensive guide to handling WebSocket errors, including connection issues, message errors, and recovery strategies.

Error Categories

Connection Errors

Errors related to WebSocket connection establishment and maintenance.

Authentication Errors

Errors related to API authentication and authorization.

Message Errors

Errors related to message parsing, validation, and processing.

Subscription Errors

Errors related to channel subscriptions and data access.

Connection Errors

Connection Refused

Error Code: ECONNREFUSED

Description: Server is not accepting connections.

Possible Causes:

  • Server is down or restarting
  • Network connectivity issues
  • Firewall blocking connections
  • Incorrect WebSocket URL

Handling:

ws.on("error", (error) => {
  if (error.code === "ECONNREFUSED") {
    console.log("Connection refused - server may be down");
    // Implement exponential backoff retry
    scheduleReconnect();
  }
});

DNS Resolution Failed

Error Code: ENOTFOUND

Description: Cannot resolve WebSocket hostname.

Possible Causes:

  • Incorrect hostname
  • DNS server issues
  • Network connectivity problems

Handling:

ws.on("error", (error) => {
  if (error.code === "ENOTFOUND") {
    console.log("DNS resolution failed");
    // Check hostname and network connectivity
    validateConnection();
  }
});

Connection Timeout

Error Code: ETIMEDOUT

Description: Connection attempt timed out.

Possible Causes:

  • Slow network connection
  • Server overload
  • Firewall delays

Handling:

ws.on("error", (error) => {
  if (error.code === "ETIMEDOUT") {
    console.log("Connection timeout");
    // Retry with longer timeout
    retryWithLongerTimeout();
  }
});

Connection Closed Unexpectedly

Error Code: ECONNABORTED

Description: Connection was closed unexpectedly.

Possible Causes:

  • Network interruption
  • Server restart
  • Authentication failure

Handling:

ws.on("close", (code, reason) => {
  console.log(`Connection closed: ${code} ${reason}`);

  if (code === 1006) {
    // Abnormal closure - attempt reconnection
    scheduleReconnect();
  } else if (code === 1000) {
    // Normal closure
    console.log("Connection closed normally");
  }
});

Authentication Errors

Invalid API Key

Error Code: invalid_api_key

Description: The provided API key is invalid or expired.

Response:

{
  "type": "auth_error",
  "error": "invalid_api_key",
  "message": "The provided API key is invalid or expired",
  "code": 401
}

Handling:

function handleMessage(message) {
  if (message.type === "auth_error" && message.error === "invalid_api_key") {
    console.error("Invalid API key");
    // Prompt user to update API credentials
    promptForNewCredentials();
  }
}

Invalid Signature

Error Code: invalid_signature

Description: The request signature is invalid.

Response:

{
  "type": "auth_error",
  "error": "invalid_signature",
  "message": "The request signature is invalid",
  "code": 401
}

Handling:

function handleMessage(message) {
  if (message.type === "auth_error" && message.error === "invalid_signature") {
    console.error("Invalid signature");
    // Check signature generation logic
    validateSignatureGeneration();
  }
}

Timestamp Too Old

Error Code: timestamp_too_old

Description: The request timestamp is outside the acceptable time window.

Response:

{
  "type": "auth_error",
  "error": "timestamp_too_old",
  "message": "The request timestamp is outside the acceptable time window",
  "code": 401
}

Handling:

function handleMessage(message) {
  if (message.type === "auth_error" && message.error === "timestamp_too_old") {
    console.error("Timestamp too old");
    // Sync system time or regenerate timestamp
    syncSystemTime();
  }
}

Insufficient Permissions

Error Code: insufficient_permissions

Description: API key does not have required permissions.

Response:

{
  "type": "auth_error",
  "error": "insufficient_permissions",
  "message": "Your API key does not have permission to access this resource",
  "code": 403
}

Handling:

function handleMessage(message) {
  if (
    message.type === "auth_error" &&
    message.error === "insufficient_permissions"
  ) {
    console.error("Insufficient permissions");
    // Check API key permissions or upgrade account
    checkPermissions();
  }
}

Message Errors

Invalid Message Format

Error Code: invalid_message_format

Description: Message does not conform to expected format.

Response:

{
  "type": "error",
  "error": "invalid_message_format",
  "message": "Message format is invalid",
  "code": 400
}

Handling:

function sendMessage(message) {
  try {
    // Validate message format before sending
    validateMessage(message);
    ws.send(JSON.stringify(message));
  } catch (error) {
    console.error("Invalid message format:", error);
    // Fix message format and retry
    fixMessageFormat(message);
  }
}

Message Too Large

Error Code: message_too_large

Description: Message exceeds size limit.

Response:

{
  "type": "error",
  "error": "message_too_large",
  "message": "Message exceeds maximum size limit of 64KB",
  "code": 413
}

Handling:

function sendMessage(message) {
  const messageSize = JSON.stringify(message).length;

  if (messageSize > 65536) {
    // 64KB
    console.error("Message too large");
    // Split message or reduce data
    splitMessage(message);
  } else {
    ws.send(JSON.stringify(message));
  }
}

Invalid JSON

Error Code: invalid_json

Description: Message contains invalid JSON.

Response:

{
  "type": "error",
  "error": "invalid_json",
  "message": "Message contains invalid JSON",
  "code": 400
}

Handling:

function handleMessage(rawMessage) {
  try {
    const message = JSON.parse(rawMessage);
    processMessage(message);
  } catch (error) {
    console.error("Invalid JSON:", error);
    console.log("Raw message:", rawMessage);
    // Log error and continue
  }
}

Subscription Errors

Invalid Symbol

Error Code: invalid_symbol

Description: The trading pair symbol is not supported.

Response:

{
  "type": "subscription_error",
  "channel": "ticker",
  "symbol": "INVALID/USD",
  "error": "invalid_symbol",
  "message": "The symbol 'INVALID/USD' is not supported",
  "code": 400
}

Handling:

function handleMessage(message) {
  if (
    message.type === "subscription_error" &&
    message.error === "invalid_symbol"
  ) {
    console.error(`Invalid symbol: ${message.symbol}`);
    // Remove invalid symbol from subscriptions
    removeInvalidSymbol(message.symbol);
  }
}

Channel Not Available

Error Code: channel_not_available

Description: The requested channel is not available.

Response:

{
  "type": "subscription_error",
  "channel": "invalid_channel",
  "error": "channel_not_available",
  "message": "The channel 'invalid_channel' is not available",
  "code": 400
}

Handling:

function handleMessage(message) {
  if (
    message.type === "subscription_error" &&
    message.error === "channel_not_available"
  ) {
    console.error(`Channel not available: ${message.channel}`);
    // Use alternative channel or fallback
    useAlternativeChannel(message.channel);
  }
}

Subscription Limit Exceeded

Error Code: subscription_limit_exceeded

Description: Maximum number of subscriptions reached.

Response:

{
  "type": "subscription_error",
  "error": "subscription_limit_exceeded",
  "message": "Maximum number of subscriptions (50) reached",
  "code": 429
}

Handling:

function handleMessage(message) {
  if (
    message.type === "subscription_error" &&
    message.error === "subscription_limit_exceeded"
  ) {
    console.error("Subscription limit exceeded");
    // Unsubscribe from least important channels
    optimizeSubscriptions();
  }
}

Rate Limiting Errors

Rate Limit Exceeded

Error Code: rate_limit_exceeded

Description: Too many messages sent too quickly.

Response:

{
  "type": "error",
  "error": "rate_limit_exceeded",
  "message": "Too many messages. Please slow down.",
  "code": 429,
  "retryAfter": 60
}

Handling:

function handleMessage(message) {
  if (message.type === "error" && message.error === "rate_limit_exceeded") {
    console.error("Rate limit exceeded");
    const retryAfter = message.retryAfter || 60;

    // Implement backoff strategy
    implementBackoff(retryAfter);
  }
}

Error Recovery Strategies

Exponential Backoff

class ReconnectionManager {
  constructor() {
    this.attempts = 0;
    this.maxAttempts = 10;
    this.baseDelay = 1000; // 1 second
    this.maxDelay = 30000; // 30 seconds
  }

  scheduleReconnect() {
    if (this.attempts >= this.maxAttempts) {
      console.error("Max reconnection attempts reached");
      return;
    }

    this.attempts++;
    const delay = Math.min(
      this.baseDelay * Math.pow(2, this.attempts - 1),
      this.maxDelay
    );

    console.log(`Reconnecting in ${delay}ms (attempt ${this.attempts})`);

    setTimeout(() => {
      this.connect();
    }, delay);
  }

  resetAttempts() {
    this.attempts = 0;
  }
}

Circuit Breaker Pattern

class CircuitBreaker {
  constructor(threshold = 5, timeout = 60000) {
    this.threshold = threshold;
    this.timeout = timeout;
    this.failureCount = 0;
    this.lastFailureTime = null;
    this.state = "CLOSED"; // CLOSED, OPEN, HALF_OPEN
  }

  canExecute() {
    if (this.state === "CLOSED") {
      return true;
    }

    if (this.state === "OPEN") {
      if (Date.now() - this.lastFailureTime > this.timeout) {
        this.state = "HALF_OPEN";
        return true;
      }
      return false;
    }

    if (this.state === "HALF_OPEN") {
      return true;
    }

    return false;
  }

  onSuccess() {
    this.failureCount = 0;
    this.state = "CLOSED";
  }

  onFailure() {
    this.failureCount++;
    this.lastFailureTime = Date.now();

    if (this.failureCount >= this.threshold) {
      this.state = "OPEN";
    }
  }
}

Message Queue for Failed Messages

class MessageQueue {
  constructor() {
    this.queue = [];
    this.maxSize = 1000;
  }

  enqueue(message) {
    if (this.queue.length >= this.maxSize) {
      // Remove oldest message
      this.queue.shift();
    }
    this.queue.push({
      message,
      timestamp: Date.now(),
      attempts: 0,
    });
  }

  dequeue() {
    return this.queue.shift();
  }

  isEmpty() {
    return this.queue.length === 0;
  }

  retryFailedMessages() {
    const now = Date.now();
    const retryDelay = 5000; // 5 seconds

    this.queue.forEach((item) => {
      if (now - item.timestamp > retryDelay && item.attempts < 3) {
        item.attempts++;
        this.sendMessage(item.message);
      }
    });
  }
}

Error Monitoring and Logging

Comprehensive Error Logging

class ErrorLogger {
  constructor() {
    this.errors = [];
    this.maxErrors = 1000;
  }

  logError(error, context = {}) {
    const errorEntry = {
      timestamp: new Date().toISOString(),
      error: {
        message: error.message,
        code: error.code,
        stack: error.stack,
      },
      context,
      userAgent: navigator.userAgent,
      url: window.location.href,
    };

    this.errors.push(errorEntry);

    if (this.errors.length > this.maxErrors) {
      this.errors.shift();
    }

    // Send to monitoring service
    this.sendToMonitoring(errorEntry);
  }

  sendToMonitoring(errorEntry) {
    // Send to external monitoring service
    fetch("/api/errors", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(errorEntry),
    }).catch((err) => {
      console.error("Failed to send error to monitoring:", err);
    });
  }
}

Error Metrics

class ErrorMetrics {
  constructor() {
    this.metrics = {
      connectionErrors: 0,
      authenticationErrors: 0,
      messageErrors: 0,
      subscriptionErrors: 0,
      rateLimitErrors: 0,
    };
  }

  incrementError(type) {
    if (this.metrics[type] !== undefined) {
      this.metrics[type]++;
    }
  }

  getMetrics() {
    return {
      ...this.metrics,
      totalErrors: Object.values(this.metrics).reduce(
        (sum, count) => sum + count,
        0
      ),
    };
  }
}

Best Practices

Error Handling Checklist

  • ✅ Implement comprehensive error logging
  • ✅ Use exponential backoff for reconnections
  • ✅ Validate all incoming messages
  • ✅ Handle rate limiting gracefully
  • ✅ Implement circuit breaker pattern
  • ✅ Monitor error metrics
  • ✅ Provide user-friendly error messages
  • ✅ Test error scenarios thoroughly

Error Recovery Patterns

  • Retry Logic: Implement smart retry with backoff
  • Fallback Mechanisms: Use alternative data sources
  • Graceful Degradation: Continue operation with reduced functionality
  • User Notification: Inform users of issues and recovery status
  • Health Checks: Monitor system health and connectivity

Need help with WebSocket connections? Check out our Connection Guide or explore Event Handling.