Skip to content

Commit 623d670

Browse files
committed
fix: Handle WebSocket disconnection gracefully
- Wrap all websocket.send_json in try/except - Prevent crash when client disconnects during indexing - Continue indexing even if client disconnects
1 parent f1048a4 commit 623d670

2 files changed

Lines changed: 43 additions & 26 deletions

File tree

backend/main.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -413,13 +413,16 @@ async def websocket_index(websocket: WebSocket, repo_id: str):
413413

414414
# Index with progress callback
415415
async def progress_callback(files_processed: int, functions_indexed: int, total_files: int):
416-
await websocket.send_json({
417-
"type": "progress",
418-
"files_processed": files_processed,
419-
"functions_indexed": functions_indexed,
420-
"total_files": total_files,
421-
"progress_pct": int((files_processed / total_files) * 100) if total_files > 0 else 0
422-
})
416+
try:
417+
await websocket.send_json({
418+
"type": "progress",
419+
"files_processed": files_processed,
420+
"functions_indexed": functions_indexed,
421+
"total_files": total_files,
422+
"progress_pct": int((files_processed / total_files) * 100) if total_files > 0 else 0
423+
})
424+
except Exception:
425+
pass # Client disconnected, continue indexing anyway
423426

424427
# Index repository with progress
425428
total_functions = await indexer.index_repository_with_progress(
@@ -432,18 +435,27 @@ async def progress_callback(files_processed: int, functions_indexed: int, total_
432435
repo_manager.update_file_count(repo_id, total_functions)
433436

434437
# Send completion
435-
await websocket.send_json({
436-
"type": "complete",
437-
"total_functions": total_functions
438-
})
438+
try:
439+
await websocket.send_json({
440+
"type": "complete",
441+
"total_functions": total_functions
442+
})
443+
except Exception:
444+
pass # Client disconnected
439445

440446
except WebSocketDisconnect:
441447
print(f"WebSocket disconnected for repo {repo_id}")
442448
except Exception as e:
443-
await websocket.send_json({"type": "error", "message": str(e)})
449+
try:
450+
await websocket.send_json({"type": "error", "message": str(e)})
451+
except Exception:
452+
pass # Connection already closed
444453
repo_manager.update_status(repo_id, "error")
445454
finally:
446-
await websocket.close()
455+
try:
456+
await websocket.close()
457+
except Exception:
458+
pass # Already closed
447459

448460

449461
@app.post("/api/repos/{repo_id}/index")

frontend/src/components/RepoOverview.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function RepoOverview({ repo, onReindex, apiUrl, apiKey }: RepoOverviewPr
2222
const [indexing, setIndexing] = useState(false)
2323
const [progress, setProgress] = useState<IndexProgress | null>(null)
2424
const wsRef = useRef<WebSocket | null>(null)
25+
const completedRef = useRef(false) // Track if indexing completed successfully
2526

2627
// Cleanup WebSocket on unmount
2728
useEffect(() => {
@@ -35,6 +36,7 @@ export function RepoOverview({ repo, onReindex, apiUrl, apiKey }: RepoOverviewPr
3536
const handleReindex = async () => {
3637
setIndexing(true)
3738
setProgress({ files_processed: 0, functions_indexed: 0, total_files: 0, progress_pct: 0 })
39+
completedRef.current = false
3840

3941
// Connect to WebSocket for real-time progress
4042
const wsUrl = `${WS_URL}/ws/index/${repo.id}?token=${apiKey}`
@@ -58,45 +60,47 @@ export function RepoOverview({ repo, onReindex, apiUrl, apiKey }: RepoOverviewPr
5860
progress_pct: data.progress_pct
5961
})
6062
} else if (data.type === 'complete') {
63+
completedRef.current = true
6164
toast.success(`Indexing complete! ${data.total_functions} functions indexed.`, { id: 'reindex' })
6265
setIndexing(false)
63-
setProgress(null) // Clear progress bar
64-
onReindex() // Refresh repo data
66+
setProgress(null)
67+
onReindex()
6568
} else if (data.type === 'error') {
69+
completedRef.current = true
6670
toast.error(`Indexing failed: ${data.message}`, { id: 'reindex' })
6771
setIndexing(false)
6872
setProgress(null)
6973
}
7074
}
7175

7276
ws.onerror = () => {
73-
// WebSocket error - fall back to HTTP
74-
toast.dismiss('reindex')
75-
fallbackToHttp()
77+
if (!completedRef.current) {
78+
toast.dismiss('reindex')
79+
fallbackToHttp()
80+
}
7681
}
7782

78-
ws.onclose = (event) => {
79-
if (event.code !== 1000 && indexing) {
80-
// Abnormal close while still indexing - fall back to HTTP
83+
ws.onclose = () => {
84+
// Only fallback if we didn't complete successfully
85+
if (!completedRef.current) {
8186
fallbackToHttp()
8287
}
8388
}
8489

8590
} catch {
86-
// WebSocket connection failed - fall back to HTTP
8791
fallbackToHttp()
8892
}
8993
}
9094

9195
const fallbackToHttp = async () => {
92-
// Fallback: Use HTTP endpoint with simulated progress
96+
if (completedRef.current) return // Already completed
97+
9398
toast.loading('Using fallback indexing...', { id: 'reindex' })
9499

95100
try {
96101
await onReindex()
97102
toast.success('Re-indexing started!', { id: 'reindex' })
98103

99-
// Simulate progress for HTTP fallback
100104
let pct = 10
101105
const interval = setInterval(() => {
102106
pct = Math.min(pct + 10, 90)
@@ -105,8 +109,9 @@ export function RepoOverview({ repo, onReindex, apiUrl, apiKey }: RepoOverviewPr
105109

106110
setTimeout(() => {
107111
clearInterval(interval)
108-
setProgress(null) // Clear progress bar
112+
setProgress(null)
109113
setIndexing(false)
114+
completedRef.current = true
110115
}, 8000)
111116

112117
} catch {
@@ -150,7 +155,7 @@ export function RepoOverview({ repo, onReindex, apiUrl, apiKey }: RepoOverviewPr
150155
</div>
151156
</div>
152157

153-
{/* Indexing Progress */}
158+
{/* Indexing Progress - only show when indexing AND progress exists */}
154159
{indexing && progress && (
155160
<div className="card p-6 border-2 border-blue-500 bg-blue-50">
156161
<div className="flex items-center justify-between mb-3">

0 commit comments

Comments
 (0)