CLAUDE: Fix infinite WebSocket reconnection loop (HIGH-002)
After max reconnection attempts (10), the stuck state detector was continuing to retry forever. Now: - Add permanentlyFailed state flag to track when we've given up - Set flag and stop stuck state detector when max attempts reached - Add manualRetry() method for UI to reset state and try again - Expose permanentlyFailed in public API for error boundary (HIGH-001) Also marks CRIT-002 and CRIT-002-BACKEND as completed in project plan. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
89a63af2a8
commit
1a7e464990
@ -47,8 +47,8 @@
|
||||
"description": "myTeamId is hardcoded to null with a TODO comment, breaking the substitution panel's ability to determine which team's lineup to display. Users cannot see or manage their own team's substitutions.",
|
||||
"category": "critical",
|
||||
"priority": 2,
|
||||
"completed": false,
|
||||
"tested": false,
|
||||
"completed": true,
|
||||
"tested": true,
|
||||
"dependencies": ["CRIT-002-BACKEND"],
|
||||
"files": [
|
||||
{
|
||||
@ -67,8 +67,8 @@
|
||||
"description": "Backend dependency for CRIT-002. The /api/auth/me endpoint needs to return the user's team ownership information so frontend can determine if user owns home/away team.",
|
||||
"category": "critical",
|
||||
"priority": 2,
|
||||
"completed": false,
|
||||
"tested": false,
|
||||
"completed": true,
|
||||
"tested": true,
|
||||
"dependencies": [],
|
||||
"files": [
|
||||
{
|
||||
|
||||
@ -85,6 +85,7 @@ const isConnected = ref(false)
|
||||
const isConnecting = ref(false)
|
||||
const connectionError = ref<string | null>(null)
|
||||
const lastConnectionAttempt = ref<number | null>(null)
|
||||
const permanentlyFailed = ref(false) // Set when max reconnection attempts reached
|
||||
|
||||
// Reset reactive state on client hydration to ensure clean slate
|
||||
if (import.meta.client) {
|
||||
@ -93,6 +94,7 @@ if (import.meta.client) {
|
||||
isConnecting.value = false
|
||||
connectionError.value = null
|
||||
lastConnectionAttempt.value = null
|
||||
permanentlyFailed.value = false
|
||||
console.log('[WebSocket] Reactive state reset on client hydration')
|
||||
}
|
||||
|
||||
@ -133,6 +135,7 @@ export function useWebSocket() {
|
||||
const state = getClientState()
|
||||
return (
|
||||
!isConnected.value &&
|
||||
!permanentlyFailed.value &&
|
||||
canConnect.value &&
|
||||
state.reconnectionAttempts < MAX_RECONNECTION_ATTEMPTS
|
||||
)
|
||||
@ -252,6 +255,7 @@ export function useWebSocket() {
|
||||
isConnected.value = false
|
||||
isConnecting.value = false
|
||||
connectionError.value = null
|
||||
permanentlyFailed.value = false
|
||||
state.reconnectionAttempts = 0
|
||||
}
|
||||
|
||||
@ -283,6 +287,7 @@ export function useWebSocket() {
|
||||
isConnected.value = false
|
||||
isConnecting.value = false
|
||||
connectionError.value = null
|
||||
permanentlyFailed.value = false
|
||||
state.reconnectionAttempts = 0
|
||||
|
||||
// Reconnect after a short delay
|
||||
@ -291,6 +296,30 @@ export function useWebSocket() {
|
||||
}, 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* Manual retry after permanent failure.
|
||||
* Resets all connection state and attempts fresh connection.
|
||||
* Use this from UI when user clicks "Retry" after max attempts reached.
|
||||
*/
|
||||
function manualRetry() {
|
||||
// SSR guard
|
||||
if (!import.meta.client) return
|
||||
|
||||
const state = getClientState()
|
||||
console.log('[WebSocket] Manual retry requested - resetting failed state')
|
||||
|
||||
// Reset all state
|
||||
permanentlyFailed.value = false
|
||||
connectionError.value = null
|
||||
state.reconnectionAttempts = 0
|
||||
|
||||
// Restart stuck state detection
|
||||
startStuckStateDetection()
|
||||
|
||||
// Attempt connection
|
||||
forceReconnect()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconnect with exponential backoff
|
||||
*/
|
||||
@ -300,8 +329,18 @@ export function useWebSocket() {
|
||||
|
||||
const state = getClientState()
|
||||
|
||||
// Check if we've hit the max attempts
|
||||
if (state.reconnectionAttempts >= MAX_RECONNECTION_ATTEMPTS) {
|
||||
console.error(`[WebSocket] Max reconnection attempts (${MAX_RECONNECTION_ATTEMPTS}) reached - giving up`)
|
||||
permanentlyFailed.value = true
|
||||
connectionError.value = `Connection failed after ${MAX_RECONNECTION_ATTEMPTS} attempts. Click retry to try again.`
|
||||
// Stop the stuck state detector since we've given up
|
||||
stopStuckStateDetection()
|
||||
return
|
||||
}
|
||||
|
||||
if (!shouldReconnect.value) {
|
||||
console.log('[WebSocket] Reconnection not needed or max attempts reached')
|
||||
console.log('[WebSocket] Reconnection not needed')
|
||||
return
|
||||
}
|
||||
|
||||
@ -690,6 +729,12 @@ export function useWebSocket() {
|
||||
if (state.stuckStateCheckInterval) return
|
||||
|
||||
state.stuckStateCheckInterval = setInterval(() => {
|
||||
// Don't attempt if we've permanently failed - user must manually retry
|
||||
if (permanentlyFailed.value) {
|
||||
console.log('[WebSocket] Stuck state check skipped - permanently failed')
|
||||
return
|
||||
}
|
||||
|
||||
// Try to connect if not connected - don't check auth state
|
||||
// Backend will authenticate via cookies
|
||||
if (!isConnected.value && !isConnecting.value) {
|
||||
@ -754,11 +799,13 @@ export function useWebSocket() {
|
||||
isConnected: readonly(isConnected),
|
||||
isConnecting: readonly(isConnecting),
|
||||
connectionError: readonly(connectionError),
|
||||
permanentlyFailed: readonly(permanentlyFailed),
|
||||
canConnect,
|
||||
|
||||
// Actions
|
||||
connect,
|
||||
disconnect,
|
||||
forceReconnect,
|
||||
manualRetry,
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user