Troubleshooting & Debugging

Common issues, debugging tips, rate limits, and security best practices

Troubleshooting & Debugging

Solutions to common issues, debugging techniques, and best practices for production deployments.

Common Issues

Connection Problems

SymptomPossible CauseSolution
Connection rejected immediatelyAuthentication required or invalid tokenCheck token validity; verify server auth mode (public/hybrid/private)
Connection timeoutNetwork issue, firewall, or wrong URLVerify URL, check firewall rules, test with curl
Repeated reconnection attemptsServer rejecting connection; invalid credentialsCheck logs for auth errors; refresh token if expired
Connection succeeds but immediately disconnectsServer-side validation failureCheck server logs; verify token has required permissions
⚠️

Authentication Modes

SkySpy supports three auth modes: public (no auth), hybrid (optional auth), and private (auth required). Check your server's WS_AUTH_MODE setting.

Subscription Issues

SymptomPossible CauseSolution
No events after connectNot subscribed to topicsEmit subscribe with desired topics after connect event
No aircraft:snapshot on connectListener attached after event firedAttach listeners before connecting, or request aircraft-snapshot
subscribed shows denied topicsMissing permissions for those topicsCheck user/API key permissions; upgrade account if needed
Events stop after a whileSubscription lost on reconnectResubscribe in connect handler (subscriptions don't persist)

Data Issues

SymptomPossible CauseSolution
Delayed updatesRate limiting or batchingExpected behavior; critical events are not batched
Missing aircraft fieldsNot all fields available from ADS-BCheck for null/undefined; use fallbacks
ACARS not received on main namespaceServer sends ACARS only to /acars namespaceConnect to /acars namespace or check server config
Stale aircraft not removedNot handling aircraft:remove eventListen for aircraft:remove and update local state

Request/Response Issues

SymptomPossible CauseSolution
Request timeoutServer slow or not respondingIncrease timeout; check server logs
Error: "Unknown request type"Typo in request type or unsupported typeCheck spelling; see supported request types in docs
Error: "Missing parameter"Required param not providedCheck request type requirements; include all required params
Error: "Permission denied"User lacks permission for request typeAuthenticate or check user/API key permissions

Debugging

Enable Client Debug Logs

// Browser (localStorage)
localStorage.setItem('debug', 'socket.io-client:*');

// Then reload the page
# Node.js (environment variable)
DEBUG=socket.io-client:* node app.js
# Python (logging)
import logging

logging.basicConfig(level=logging.DEBUG)
logging.getLogger('socketio').setLevel(logging.DEBUG)
logging.getLogger('engineio').setLevel(logging.DEBUG)

Server-Side Debugging

Add debug logging to Django settings:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'INFO',
    },
    'loggers': {
        'skyspy.socketio': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
        'socketio': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
        'engineio': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}

Network Inspection

Browser DevTools

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Filter by WS (WebSocket)
  4. Select Socket.IO connection
  5. View Messages tab for frame-by-frame inspection

Command Line

# Test HTTP endpoint
curl -v https://skyspy.example.com/socket.io/

# Test with authentication
curl -v -H "Authorization: Bearer YOUR_TOKEN" \
  https://skyspy.example.com/socket.io/

# Check if WebSocket upgrade succeeds
wscat -c wss://skyspy.example.com/socket.io/?EIO=4&transport=websocket

Logging Events

Create a debug client that logs all events:

const socket = io('https://skyspy.example.com', {
  auth: { token: 'YOUR_TOKEN' }
});

// Log all incoming events
const originalOn = socket.on.bind(socket);
socket.on = function(event, callback) {
  return originalOn(event, function(...args) {
    console.log(`[Event] ${event}:`, args);
    return callback(...args);
  });
};

// Log all outgoing events
const originalEmit = socket.emit.bind(socket);
socket.emit = function(event, ...args) {
  console.log(`[Emit] ${event}:`, args);
  return originalEmit(event, ...args);
};
import socketio
import logging

logger = logging.getLogger(__name__)

class DebugClient(socketio.Client):
    def emit(self, event, data=None, *args, **kwargs):
        logger.debug(f'[Emit] {event}: {data}')
        return super().emit(event, data, *args, **kwargs)
    
    def on(self, event, handler=None):
        def wrapper(data):
            logger.debug(f'[Event] {event}: {data}')
            if handler:
                return handler(data)
        return super().on(event, wrapper)

sio = DebugClient()
logging.basicConfig(level=logging.DEBUG)

Rate Limits

Understanding and working with rate limits.

Default Rate Limits

Topic / EventMax RateMin IntervalBatching
aircraft:update~10 Hz100 msYes (200ms window)
aircraft:delta~10 Hz100 msYes (200ms window)
stats:update~0.5 Hz2 sYes
safety:eventNo limitNo (critical)
alert:triggeredNo limitNo (critical)
Default~5 Hz200 msYes

Critical Events

Safety events, alerts, and emergency events bypass batching and rate limits to ensure immediate delivery.

Client-Side Throttling

If your UI can't keep up with updates, implement client-side throttling:

import { throttle } from 'lodash';

// Throttle map updates to 30 FPS
const throttledRender = throttle((aircraft) => {
  renderMap(aircraft);
}, 1000 / 30);

socket.on('aircraft:update', (aircraft) => {
  // Update state immediately
  updateAircraftState(aircraft);
  
  // Throttle rendering
  throttledRender(getAircraft());
});

// Or use requestAnimationFrame
let updatePending = false;

socket.on('aircraft:update', (aircraft) => {
  updateAircraftState(aircraft);
  
  if (!updatePending) {
    updatePending = true;
    requestAnimationFrame(() => {
      renderMap(getAircraft());
      updatePending = false;
    });
  }
});

Security

Best Practices

⚠️

Production Security Checklist

Follow these guidelines for secure production deployments:

PracticeRationaleImplementation
Use TLS (HTTPS/WSS)Encrypt credentials and data in transitUse https:// URLs; configure SSL certificates
Pass tokens in auth objectQuery strings are often loggedUse auth: { token } in Socket.IO options
Rotate tokens regularlyLimit exposure window if token leaksUse short-lived JWTs; refresh before expiry
Use API keys for servicesBetter access control than user tokensGenerate API keys in dashboard; use sk_live_* prefix
Validate permissionsEnforce least-privilege accessCheck user/API key permissions on server
Rate limit connectionsPrevent abuse and DoS attacksConfigure per-IP connection limits
Monitor for anomaliesDetect compromised tokens or attacksLog suspicious activity; alert on unusual patterns

Token Expiry Handling

Handle JWT expiration gracefully:

class SkySpyClient {
  constructor(url, getToken) {
    this.url = url;
    this.getToken = getToken; // Function to get/refresh token
    this.socket = null;
    this.connect();
  }

  connect() {
    const token = this.getToken();
    
    this.socket = io(this.url, {
      auth: { token },
      transports: ['websocket']
    });

    this.socket.on('connect_error', (error) => {
      if (error.message.includes('auth') || error.message.includes('token')) {
        console.log('Auth error, refreshing token...');
        this.refreshAndReconnect();
      }
    });

    this.socket.on('disconnect', (reason) => {
      if (reason === 'io server disconnect') {
        // Server disconnected (possibly auth failure)
        console.log('Server disconnect, refreshing token...');
        this.refreshAndReconnect();
      }
    });
  }

  async refreshAndReconnect() {
    try {
      // Refresh token (e.g., call refresh endpoint)
      const newToken = await this.refreshToken();
      
      // Disconnect old socket
      if (this.socket) {
        this.socket.disconnect();
      }
      
      // Reconnect with new token
      this.connect();
    } catch (error) {
      console.error('Token refresh failed:', error);
      // Redirect to login or show error
    }
  }

  async refreshToken() {
    const response = await fetch('/api/auth/refresh/', {
      method: 'POST',
      credentials: 'include' // Send refresh cookie
    });
    
    if (!response.ok) {
      throw new Error('Failed to refresh token');
    }
    
    const data = await response.json();
    return data.access_token;
  }
}

Secure Token Storage

EnvironmentStorage MethodSecurity Notes
Browser (Web App)Memory variable or sessionStorageAvoid localStorage (XSS risk); use httpOnly cookies for refresh tokens
Mobile AppSecure enclave / keychainUse iOS Keychain or Android Keystore
Server / CLIEnvironment variables or config fileRestrict file permissions (600); never commit to git
Desktop AppOS credential managerUse platform APIs (e.g., Windows Credential Manager)

Error Messages Reference

Common error messages and their meanings:

Error MessageMeaningResolution
"Invalid JSON"Malformed payloadCheck payload format; ensure valid JSON
"Unknown action"Unsupported event nameUse supported events (subscribe, request, etc.)
"Unknown request type"Invalid request type in request eventCheck spelling; see supported request types
"Missing parameter: X"Required parameter not providedInclude parameter X in request params
"Permission denied"User lacks permission for topic or requestAuthenticate or check permissions
"Invalid token"Token is invalid or expiredRefresh token and reconnect
"Authentication required"Server in private mode; auth requiredProvide valid token in auth object
"Rate limit exceeded"Too many requests or connectionsSlow down; implement backoff

Performance Optimization

Reduce Bandwidth

TechniqueDescriptionSavings
Selective subscriptionsSubscribe only to needed topics50-80% (vs. all)
Delta updatesUse aircraft:delta instead of full updates30-60% (payload size)
Filter on serverUse request params to filter resultsVaries by query
CompressionEnable gzip/brotli on server60-80% (text data)

Reduce CPU Usage

TechniqueDescriptionImpact
Throttle renderingRender at 30 FPS instead of on every update70-90% (CPU)
Debounce searchesWait for user to stop typing before searchingReduces unnecessary requests
Virtualize listsOnly render visible items in large lists90%+ for large lists
Web WorkersProcess data in background threadPrevents UI blocking

Need More Help?

📘

Additional Resources