WebSocket Connection Closes Before Establishment - Troubleshooting Guide
Overview
When a WebSocket connection closes immediately after creation (before establishment), it typically indicates one of several common issues. This guide helps diagnose and resolve these problems.
Common Causes & Solutions
1. Server Rejection (Most Common)
Symptoms:
- Connection closes within 0-100ms
- Close code: 1002 (Protocol error), 1008 (Policy violation), or 1011 (Server error)
- Console logs show immediate closure
Possible Causes:
// Invalid endpoint
wss://pipeline.aiqlick.com/ws/pipeline/invalid-client-id
// Authentication failure
wss://pipeline.aiqlick.com/ws/pipeline/unauthorized-client
// Server overload/rate limiting
// Server returns 429 Too Many Requests equivalent
Solutions:
- Verify the WebSocket endpoint URL is correct
- Check client ID format and authentication
- Implement exponential backoff (already implemented)
- Contact server administrator about rate limiting
2. Network/Firewall Issues
Symptoms:
- Connection closes within 100-1000ms
- Close code: 1006 (Abnormal closure)
- Network errors in browser console
Possible Causes:
- Corporate firewall blocking WebSocket connections
- Proxy server not supporting WebSocket upgrade
- Network instability or packet loss
Solutions:
// Test basic connectivity first
const testConnectivity = async () => {
try {
await fetch("https://pipeline.aiqlick.com", {
method: "HEAD",
signal: AbortSignal.timeout(5000),
})
console.log("Basic HTTP connectivity: OK")
} catch (error) {
console.error("Basic connectivity failed:", error)
}
}
3. SSL/TLS Issues
Symptoms:
- Close code: 1015 (TLS handshake failed)
- Browser security errors
- Mixed content warnings
Possible Causes:
- Connecting to WSS from HTTP page
- Invalid SSL certificate
- Outdated TLS version
Solutions:
// Check protocol compatibility
if (location.protocol === "http:" && wsUrl.startsWith("wss:")) {
console.error("Cannot connect to secure WebSocket from insecure page")
// Use HTTPS or switch to ws:// (not recommended for production)
}
4. Browser/Client Issues
Symptoms:
- Immediate rejection
- Browser compatibility errors
- Resource exhaustion
Possible Causes:
- Too many concurrent WebSocket connections
- Browser WebSocket limit exceeded
- Client-side blocking (ad blockers, security software)
Solutions:
// Check WebSocket limits
if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 2) {
console.warn("Low system resources detected")
}
// Clean up existing connections
const cleanup = () => {
if (wsRef.current) {
wsRef.current.close(1000, "Cleanup")
wsRef.current = null
}
}
Enhanced Debugging Implementation
1. Connection Timing Analysis
const connectionStartTime = Date.now()
ws.onclose = (event) => {
const connectionDuration = Date.now() - connectionStartTime
if (connectionDuration < 100) {
console.error("IMMEDIATE REJECTION: Server rejected connection instantly")
console.error(
"Likely causes: Invalid endpoint, authentication failure, server overload"
)
} else if (connectionDuration < 1000) {
console.error("EARLY CLOSURE: Connection failed during handshake")
console.error("Likely causes: Network/firewall issues, SSL problems")
} else {
console.error("TIMEOUT CLOSURE: Connection attempt timed out")
console.error("Likely causes: Server overload, network congestion")
}
}
2. Detailed Error Codes
const getCloseCodeMeaning = (code: number): string => {
const meanings = {
1000: "Normal closure",
1001: "Going away (server shutdown)",
1002: "Protocol error (invalid WebSocket frame)",
1003: "Unsupported data type",
1005: "No status received (abnormal)",
1006: "Abnormal closure (network issue)",
1007: "Invalid frame payload data",
1008: "Policy violation (blocked by server)",
1009: "Message too big",
1010: "Mandatory extension missing",
1011: "Internal server error",
1012: "Service restart",
1013: "Try again later (temporary overload)",
1014: "Bad gateway (proxy issue)",
1015: "TLS handshake failed",
}
return meanings[code] || `Unknown code (${code})`
}
3. Pre-Connection Validation
const validateConnection = async (): Promise<boolean> => {
// Check basic network
if (!navigator.onLine) {
console.error("No internet connection")
return false
}
// Check protocol compatibility
if (location.protocol === "http:" && wsUrl.startsWith("wss:")) {
console.error("SSL/TLS mismatch")
return false
}
// Test HTTP connectivity
try {
await fetch(httpEquivalent, {
method: "HEAD",
signal: AbortSignal.timeout(3000),
})
} catch (error) {
console.error("HTTP connectivity test failed:", error)
return false
}
return true
}
Current Implementation Features
1. Rate Limiting Protection
- Minimum 3-second interval between attempts
- Maximum 5 reconnection attempts
- Exponential backoff: 2s, 4s, 8s, 16s, 32s
2. Comprehensive Error Logging
console.log("Connection attempt details:", {
url: wsUrl,
clientId: clientId,
attempt: reconnectCountRef.current + 1,
maxAttempts: maxReconnectAttempts,
lastAttempt: new Date(lastConnectionAttemptRef.current).toISOString(),
timeSinceLastAttempt: Date.now() - lastConnectionAttemptRef.current,
})
3. Connection Timeout Protection
- 15-second connection timeout
- Prevents browser hanging
- Clear error messaging
4. State Management
- Four-state tracking: disconnected/connecting/connected/error
- Visual indicators for each state
- Manual reconnect capability
Diagnostic Steps
Step 1: Enable Detailed Logging
// In browser console, enable verbose WebSocket logging
localStorage.setItem("debug", "websocket:*")
Step 2: Check Network Tab
- Open browser DevTools → Network tab
- Look for WebSocket connection entries
- Check HTTP status codes and timing
Step 3: Use WebSocket Diagnostic Component
import { WebSocketDiagnostic } from "@components/cv/WebSocketDiagnostic"
// Use in development to test connectivity
;<WebSocketDiagnostic />
Step 4: Server-Side Verification
# Test WebSocket endpoint directly
wscat -c wss://pipeline.aiqlick.com/ws/pipeline/test-client-id
# Check server logs for rejection reasons
docker logs pipeline-service --tail=100 | grep WebSocket
Prevention Strategies
1. Connection Pooling
// Limit concurrent connections
const maxConcurrentConnections = 3
const activeConnections = new Set<WebSocket>()
const createConnection = () => {
if (activeConnections.size >= maxConcurrentConnections) {
throw new Error("Too many concurrent connections")
}
// ... create connection
}
2. Health Checks
// Regular connectivity tests
const healthCheck = setInterval(async () => {
if (!isConnected) {
const isHealthy = await testPipelineConnectivity()
if (isHealthy && reconnectCountRef.current < maxReconnectAttempts) {
reconnect()
}
}
}, 30000)
3. Graceful Degradation
// Fallback to HTTP polling if WebSocket fails repeatedly
const fallbackToPolling = () => {
if (reconnectCountRef.current >= maxReconnectAttempts) {
console.log("Falling back to HTTP polling")
// Implement HTTP-based alternative
}
}
Server-Side Considerations
1. Rate Limiting Configuration
# Example server-side rate limiting
WEBSOCKET_RATE_LIMIT = {
'connections_per_minute': 10,
'connections_per_hour': 100,
'message_rate_per_second': 5
}
2. Proper Error Responses
# Send meaningful close codes
if not authenticate(client_id):
await websocket.close(code=1008, reason="Authentication failed")
if rate_limited(client_ip):
await websocket.close(code=1013, reason="Rate limit exceeded")
3. Health Monitoring
# Monitor connection patterns
logger.info(f"WebSocket connection attempt: {client_id} from {client_ip}")
logger.warning(f"Connection rejected: {reason}")
Testing Scenarios
1. Invalid Client ID
// Test with malformed client ID
const invalidClientId = "invalid@client#id"
2. Rapid Reconnection
// Test rate limiting
for (let i = 0; i < 10; i++) {
setTimeout(() => connect(), i * 100)
}
3. Network Interruption
// Simulate network loss
navigator.connection &&
navigator.connection.addEventListener("change", () => {
console.log("Network status changed:", navigator.onLine)
})
Summary
Early WebSocket closures are usually caused by:
- Server rejection (authentication, rate limiting, invalid endpoint)
- Network issues (firewall, proxy, connectivity)
- SSL/TLS problems (certificate, protocol mismatch)
- Browser limitations (too many connections, security policies)
The enhanced implementation provides comprehensive debugging, rate limiting, and error handling to identify and resolve these issues effectively.