Skip to content

Realtime Troubleshooting Guide

Solutions to common issues when working with Centrali Realtime.

Connection Issues

Connection Fails Immediately

Symptoms: - onError callback fires immediately - Error code: MISSING_TOKEN, INVALID_TOKEN, or WORKSPACE_MISMATCH

Solutions:

  1. Check token is provided:

    const centrali = new CentraliSDK({
      baseUrl: 'https://api.centrali.io',
      workspaceId: 'my-workspace',
      token: yourToken  // Ensure this is not null/undefined
    });
    
    // Verify token before subscribing
    console.log('Token:', centrali.getToken() ? 'present' : 'missing');
    

  2. Verify token is valid:

    // Decode JWT to inspect (don't use in production)
    const payload = JSON.parse(atob(token.split('.')[1]));
    console.log('Expires:', new Date(payload.exp * 1000));
    console.log('Workspace:', payload.workspaces);
    

  3. Check workspace matches:

    // The workspace in SDK options must match a workspace in the token
    const centrali = new CentraliSDK({
      workspaceId: 'my-workspace',  // Must be in token's workspaces claim
      token: token
    });
    

Connection Drops Frequently

Symptoms: - onDisconnected callback fires repeatedly - Events stop arriving, then resume

Solutions:

  1. Check network stability:
  2. Test with other SSE connections
  3. Check for proxy/firewall issues
  4. Verify SSL certificate is valid

  5. Monitor reconnection behavior:

    let reconnectCount = 0;
    
    centrali.realtime.subscribe({
      onConnected: () => {
        console.log(`Connected (reconnect #${reconnectCount})`);
      },
      onDisconnected: (reason) => {
        reconnectCount++;
        console.log(`Disconnected: ${reason}`);
      },
      onEvent: handleEvent
    });
    

  6. Check for connection timeout:

  7. Connections close after 1 hour by default
  8. SDK automatically reconnects
  9. If timeouts are too frequent, check server configuration

Rate Limit Exceeded

Symptoms: - Error code: RATE_LIMIT_EXCEEDED - HTTP status: 429

Solutions:

  1. Check current plan limits:
  2. Free tier: 10 concurrent connections per workspace
  3. Standard tier: 100 concurrent connections per workspace
  4. Pro tier: 1,000 concurrent connections per workspace
  5. Enterprise: Custom limits

  6. Reduce connection count:

    // Bad: Multiple subscriptions for same events
    const sub1 = centrali.realtime.subscribe({ structures: ['order'], onEvent: handle1 });
    const sub2 = centrali.realtime.subscribe({ structures: ['order'], onEvent: handle2 });
    
    // Good: Single subscription with combined handler
    const subscription = centrali.realtime.subscribe({
      structures: ['order'],
      onEvent: (event) => {
        handle1(event);
        handle2(event);
      }
    });
    

  7. Implement connection pooling for server-side applications:

    // Singleton pattern for shared realtime connection
    class RealtimePool {
      private static instance: RealtimeSubscription;
      private static handlers: Set<EventHandler> = new Set();
    
      static subscribe(handler: EventHandler) {
        this.handlers.add(handler);
        if (!this.instance) {
          this.instance = centrali.realtime.subscribe({
            onEvent: (event) => {
              this.handlers.forEach(h => h(event));
            }
          });
        }
        return () => this.handlers.delete(handler);
      }
    }
    

Event Issues

Not Receiving Any Events

Symptoms: - Connection succeeds (onConnected fires) - No events received despite data changes

Solutions:

  1. Verify events are being generated:

    # Check Redis for published events
    redis-cli PSUBSCRIBE "workspace:*:records"
    
    # Then create/update a record in another terminal
    

  2. Check structure filter:

    // Ensure structure slug matches exactly
    centrali.realtime.subscribe({
      structures: ['Order'],  // Wrong! Use lowercase slug
      structures: ['order'],  // Correct
      onEvent: handleEvent
    });
    

  3. Check event type filter:

    // If filtering, ensure you're listening for the right events
    centrali.realtime.subscribe({
      events: ['record_created'],  // Won't receive updates!
      events: ['record_created', 'record_updated'],  // Better
      onEvent: handleEvent
    });
    

  4. Verify CFL filter syntax:

    // Invalid filter will reject events
    centrali.realtime.subscribe({
      filter: 'status:shipped',       // Wrong! Missing 'data.' prefix
      filter: 'data.status:shipped',  // Correct
      onEvent: handleEvent
    });
    

Missing Events During Reconnection

Symptoms: - Events are missed when connection drops and reconnects

Explanation: Realtime is a "live" stream - events during disconnection are not replayed. This is by design for v1.

Solutions:

  1. Implement gap detection:

    let lastEventTime: Date | null = null;
    
    centrali.realtime.subscribe({
      onConnected: async () => {
        if (lastEventTime) {
          // Fetch records modified since last event
          const missed = await centrali.queryRecords('order', {
            filter: `updatedAt > ${lastEventTime.toISOString()}`
          });
          missed.data.forEach(processRecord);
        }
      },
      onEvent: (event) => {
        lastEventTime = new Date(event.timestamp);
        processEvent(event);
      }
    });
    

  2. Use periodic sync:

    // Periodically verify local state matches server
    setInterval(async () => {
      const serverState = await centrali.queryRecords('order', {
        filter: 'status in (pending, processing)'
      });
      reconcileWithLocalState(serverState.data);
    }, 60000);  // Every minute
    

Duplicate Events

Symptoms: - Same event received multiple times

Explanation: During reconnection or in rare network conditions, events may be duplicated.

Solution: Implement idempotent event handling:

const processedEvents = new Set<string>();

centrali.realtime.subscribe({
  onEvent: (event) => {
    // Create unique key for event
    const eventKey = `${event.recordId}-${event.timestamp}`;

    if (processedEvents.has(eventKey)) {
      console.log('Duplicate event ignored:', eventKey);
      return;
    }

    processedEvents.add(eventKey);

    // Prevent memory leak - keep last 1000 events
    if (processedEvents.size > 1000) {
      const oldest = processedEvents.values().next().value;
      processedEvents.delete(oldest);
    }

    processEvent(event);
  }
});

Authentication Issues

Token Expired During Session

Symptoms: - Error code: TOKEN_EXPIRED - Connection was working, then failed

Solution: Implement token refresh:

centrali.realtime.subscribe({
  onError: async (error) => {
    if (error.code === 'TOKEN_EXPIRED') {
      // Refresh your token
      const newToken = await refreshAuthToken();
      centrali.setToken(newToken);
      // SDK will automatically reconnect with new token
    }
  },
  onEvent: handleEvent
});

Service Account Token Issues

Symptoms: - Client credentials aren't working - Error fetching initial token

Solutions:

  1. Verify credentials:

    // Test credentials directly
    const tokenResponse = await fetch('https://api.centrali.io/iam/oauth/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: process.env.CENTRALI_CLIENT_ID,
        client_secret: process.env.CENTRALI_CLIENT_SECRET,
        scope: `workspace:${workspaceId}`
      })
    });
    console.log(await tokenResponse.json());
    

  2. Check service account permissions:

  3. Ensure the service account has realtime:subscribe permission
  4. Verify workspace access is granted

Filter Issues

Filter Not Working

Symptoms: - Receiving events that should be filtered out - No events received with valid-looking filter

Solutions:

  1. Verify field path:

    // Event data structure:
    // { event: "record_created", data: { status: "pending", ... } }
    
    // Wrong - missing data prefix
    filter: 'status:pending'
    
    // Correct
    filter: 'data.status:pending'
    

  2. Check operator syntax:

    // Wrong - spaces not allowed
    filter: 'data.amount : gt : 100'
    
    // Correct
    filter: 'data.amount:gt:100'
    

  3. Test filter values:

    // Test with simple equality first
    filter: 'data.id:known-id'
    
    // Then try your actual filter
    filter: 'data.status:in:pending,processing'
    

Invalid Filter Error

Symptoms: - Connection fails with 400 error - Error message mentions "invalid filter"

Solutions:

  1. Check for valid operators:
  2. Valid: eq, ne, gt, lt, gte, lte, in, nin, contains, startswith, endswith
  3. Invalid: =, !=, >, <, like, matches

  4. Escape special characters:

    // If value contains colons, they're treated as delimiters
    // Use URL encoding if needed
    filter: `data.url:${encodeURIComponent('https://example.com')}`
    

Performance Issues

High Memory Usage (Browser)

Symptoms: - Memory increases over time - Browser becomes slow

Solutions:

  1. Clean up subscriptions:

    useEffect(() => {
      const subscription = centrali.realtime.subscribe({...});
      return () => subscription.unsubscribe();  // CRITICAL!
    }, []);
    

  2. Limit stored events:

    const MAX_EVENTS = 100;
    const [events, setEvents] = useState<Event[]>([]);
    
    centrali.realtime.subscribe({
      onEvent: (event) => {
        setEvents(prev => [event, ...prev].slice(0, MAX_EVENTS));
      }
    });
    

High CPU Usage

Symptoms: - Constant CPU activity - Many reconnection attempts

Solutions:

  1. Check for reconnection loop:

    centrali.realtime.subscribe({
      onError: (error) => {
        if (!error.recoverable) {
          console.error('Fatal error, stopping:', error);
          // Don't retry - will cause infinite loop
        }
      },
      onEvent: handleEvent
    });
    

  2. Reduce event volume:

    // Add filters to reduce events
    centrali.realtime.subscribe({
      structures: ['order'],  // Not all structures
      events: ['record_updated'],  // Not all events
      filter: 'data.after.status:pending',  // Filter on new value for updates
      onEvent: handleEvent
    });
    

Debug Mode

Enable debug logging for detailed troubleshooting:

// In browser console
localStorage.setItem('DEBUG', 'centrali:*');

// Or check connection state
const sub = centrali.realtime.subscribe({...});
console.log('Connected:', sub.connected);

Getting Help

If you're still experiencing issues:

  1. Check status page for service incidents
  2. Review API logs in your Centrali dashboard
  3. Contact support with:
  4. Error codes and messages
  5. SDK version
  6. Browser/Node.js version
  7. Steps to reproduce