This guide covers advanced error handling, troubleshooting scenarios, and production-ready configuration for Amberdata’s WebSocket services.

Error Handling & Response Codes

Common Error Scenarios

1. Wildcard Instrument Subscription Error

Error Response:
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "description": "Wildcard subscription is not allowed for instruments. Please subscribe with each instrument name explicitly.",
    "code": 400
  }
}
Cause: Attempting to use "instrument": "ALL" or similar wildcard patterns. Solution:
// ❌ This will fail
{
  "params": ["market:spot:trades", {"exchange": "bitget"}]
}

// ✅ Use explicit instrument subscriptions
{
  "params": ["market:spot:trades", {"exchange": "bitget", "pair": "btc_usdt"}]
}

2. Subscription Limit Exceeded

Error Response:
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "description": "Max subscription limit reached on this connection. Please open additional connections to subscribe to more instruments.",
    "code": 400
  }
}
Solution: Implement connection pooling:
class WebSocketPool {
  constructor(maxSubsPerConnection = 100) {
    this.connections = [];
    this.maxSubs = maxSubsPerConnection;
    this.currentConnection = 0;
  }

  getConnection() {
    if (this.getSubCount(this.currentConnection) >= this.maxSubs) {
      this.currentConnection++;
      this.createConnection(this.currentConnection);
    }
    return this.connections[this.currentConnection];
  }

  subscribe(params) {
    const conn = this.getConnection();
    conn.send(JSON.stringify({
      jsonrpc: "2.0",
      id: Date.now(),
      method: "subscribe",
      params: params
    }));
  }
}

3. Invalid API Key

Error: Connection closes with message invalid api key '<api_key>' Solution: Verify API key and permissions:
const connectWithRetry = (apiKey, maxRetries = 3) => {
  let retries = 0;

  const connect = () => {
    const ws = new WebSocket('wss://ws.amberdata.com', {
      headers: {
        'x-api-key': apiKey,
        'x-amberdata-blockchain-id': 'ethereum-mainnet'
      }
    });

    ws.on('error', (error) => {
      if (error.message.includes('invalid api key') && retries < maxRetries) {
        retries++;
        console.log(`Retrying connection (${retries}/${maxRetries})...`);
        setTimeout(connect, 1000 * retries); // Exponential backoff
      } else {
        console.error('Connection failed:', error);
      }
    });

    return ws;
  };

  return connect();
};

Production Error Handling Patterns

Robust Reconnection Logic

class RobustWebSocket {
  constructor(url, options = {}) {
    this.url = url;
    this.options = options;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
    this.reconnectDelay = options.reconnectDelay || 1000;
    this.subscriptions = new Map();

    this.connect();
  }

  connect() {
    try {
      this.ws = new WebSocket(this.url, {
        headers: this.options.headers
      });

      this.ws.on('open', () => {
        console.log('WebSocket connected');
        this.reconnectAttempts = 0;
        this.resubscribeAll();
      });

      this.ws.on('message', (data) => {
        this.handleMessage(JSON.parse(data));
      });

      this.ws.on('close', () => {
        console.log('WebSocket disconnected');
        this.handleReconnect();
      });

      this.ws.on('error', (error) => {
        console.error('WebSocket error:', error);
        this.handleReconnect();
      });

    } catch (error) {
      console.error('Connection failed:', error);
      this.handleReconnect();
    }
  }

  handleReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);

      console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
      setTimeout(() => this.connect(), delay);
    } else {
      console.error('Max reconnection attempts reached');
    }
  }

  subscribe(params) {
    const id = Date.now();
    const request = {
      jsonrpc: "2.0",
      id: id,
      method: "subscribe",
      params: params
    };

    // Store subscription for reconnection
    this.subscriptions.set(id, params);

    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(request));
    }
  }

  resubscribeAll() {
    for (const [id, params] of this.subscriptions) {
      this.subscribe(params);
    }
  }

  handleMessage(message) {
    if (message.error) {
      console.error('Subscription error:', message.error);
      this.handleSubscriptionError(message);
    } else if (message.method === 'subscription') {
      this.handleSubscriptionData(message.params);
    }
  }

  handleSubscriptionError(message) {
    const { error, id } = message;

    switch (error.code) {
      case 400:
        if (error.description.includes('Wildcard subscription')) {
          console.error('Wildcard subscription not allowed for:', id);
          // Remove invalid subscription
          this.subscriptions.delete(id);
        } else if (error.description.includes('Max subscription limit')) {
          console.error('Subscription limit reached');
          // Implement connection pooling logic
          this.handleSubscriptionLimit();
        }
        break;
      default:
        console.error('Unknown subscription error:', error);
    }
  }
}

Message Processing & Buffering

class MessageProcessor {
  constructor(batchSize = 100, flushInterval = 1000) {
    this.buffer = [];
    this.batchSize = batchSize;
    this.flushInterval = flushInterval;
    this.processing = false;

    // Flush buffer periodically
    setInterval(() => this.flush(), flushInterval);
  }

  addMessage(message) {
    this.buffer.push({
      ...message,
      timestamp: Date.now()
    });

    if (this.buffer.length >= this.batchSize) {
      this.flush();
    }
  }

  async flush() {
    if (this.processing || this.buffer.length === 0) return;

    this.processing = true;
    const batch = this.buffer.splice(0, this.batchSize);

    try {
      await this.processBatch(batch);
    } catch (error) {
      console.error('Batch processing failed:', error);
      // Re-queue failed messages
      this.buffer.unshift(...batch);
    } finally {
      this.processing = false;
    }
  }

  async processBatch(messages) {
    // Process messages in batch
    const grouped = this.groupMessagesByType(messages);

    for (const [type, msgs] of Object.entries(grouped)) {
      await this.processMessageType(type, msgs);
    }
  }

  groupMessagesByType(messages) {
    return messages.reduce((groups, msg) => {
      const type = this.getMessageType(msg);
      if (!groups[type]) groups[type] = [];
      groups[type].push(msg);
      return groups;
    }, {});
  }
}

Advanced Configuration

Connection Optimization

Multiple Endpoint Strategy:
class MultiEndpointManager {
  constructor() {
    this.connections = {
      spot: new RobustWebSocket('wss://ws.amberdata.com/spot', {
        headers: { 'x-api-key': process.env.API_KEY }
      }),
      futures: new RobustWebSocket('wss://ws.amberdata.com/futures', {
        headers: { 'x-api-key': process.env.API_KEY }
      }),
      options: new RobustWebSocket('wss://ws.amberdata.com/options', {
        headers: { 'x-api-key': process.env.API_KEY }
      }),
      blockchain: new RobustWebSocket('wss://ws.amberdata.com', {
        headers: {
          'x-api-key': process.env.API_KEY,
          'x-amberdata-blockchain-id': 'ethereum-mainnet'
        }
      })
    };
  }

  subscribe(dataType, params) {
    const endpoint = this.getEndpointForDataType(dataType);
    if (endpoint) {
      this.connections[endpoint].subscribe([dataType, params]);
    } else {
      throw new Error(`No endpoint configured for data type: ${dataType}`);
    }
  }

  getEndpointForDataType(dataType) {
    if (dataType.startsWith('market:spot:')) return 'spot';
    if (dataType.startsWith('market:futures:')) return 'futures';
    if (dataType.startsWith('market:options:')) return 'options';
    if (dataType.includes('block') || dataType.includes('transaction')) return 'blockchain';
    return null;
  }
}

Health Monitoring

class ConnectionHealthMonitor {
  constructor(connections) {
    this.connections = connections;
    this.metrics = {
      messageCount: 0,
      errorCount: 0,
      lastMessageTime: Date.now(),
      connectionStatus: {}
    };

    this.startHealthChecks();
  }

  startHealthChecks() {
    // Check connection health every 30 seconds
    setInterval(() => this.performHealthCheck(), 30000);

    // Send ping every 10 seconds
    setInterval(() => this.sendPings(), 10000);
  }

  performHealthCheck() {
    const now = Date.now();
    const timeSinceLastMessage = now - this.metrics.lastMessageTime;

    // Alert if no messages in 60 seconds
    if (timeSinceLastMessage > 60000) {
      console.warn('No messages received in 60 seconds');
      this.triggerReconnection();
    }

    // Log health metrics
    console.log('Health Status:', {
      messageCount: this.metrics.messageCount,
      errorCount: this.metrics.errorCount,
      timeSinceLastMessage: timeSinceLastMessage,
      activeConnections: Object.keys(this.connections).length
    });
  }

  sendPings() {
    for (const [name, connection] of Object.entries(this.connections)) {
      if (connection.ws && connection.ws.readyState === WebSocket.OPEN) {
        connection.ws.ping();
      }
    }
  }

  recordMessage() {
    this.metrics.messageCount++;
    this.metrics.lastMessageTime = Date.now();
  }

  recordError() {
    this.metrics.errorCount++;
  }
}

Enterprise Features & Customization

Custom Rate Limits

Enterprise customers can request custom configurations:
// Example enterprise configuration request
const enterpriseConfig = {
  maxConnections: 100,
  maxSubscriptionsPerConnection: 500,
  wildcardSupport: true,
  customEndpoints: true,
  prioritySupport: true,
  dedicatedInfrastructure: true
};

// Contact sales team with requirements

Advanced Subscription Patterns

Conditional Subscriptions:
class ConditionalSubscriber {
  constructor(websocket) {
    this.ws = websocket;
    this.conditions = new Map();
  }

  subscribeWithCondition(params, condition) {
    const subscriptionId = this.ws.subscribe(params);
    this.conditions.set(subscriptionId, condition);
  }

  handleMessage(message) {
    const { subscription, result } = message.params;
    const condition = this.conditions.get(subscription);

    if (condition && condition(result)) {
      this.processMessage(result);
    }
  }
}

Troubleshooting Checklist

Connection Issues

  • Verify API key is valid and active
  • Check network connectivity and firewall settings
  • Ensure proper WebSocket library configuration
  • Verify endpoint URL is correct for data type

Subscription Issues

  • Confirm subscription parameters are valid
  • Check subscription limits haven’t been exceeded
  • Verify exchange and pair names are correct
  • Ensure proper JSON-RPC 2.0 format

Performance Issues

  • Monitor message processing latency
  • Check for memory leaks in message handling
  • Verify connection distribution across endpoints
  • Review subscription patterns for efficiency

Data Quality Issues

  • Implement message deduplication
  • Add timestamp validation
  • Monitor for missing sequence numbers
  • Verify data format expectations

Getting Support

For enterprise-level support and custom configurations:
  • Technical Issues: Contact support with connection logs and error messages
  • Custom Rate Limits: Reach out to sales team with requirements
  • Performance Optimization: Schedule consultation for high-volume use cases
  • Integration Support: Request dedicated technical account management
Enterprise customers receive priority support with guaranteed response times and dedicated infrastructure options.