From 30843295d62283ca0c4e2bf383e78a5be30176a5 Mon Sep 17 00:00:00 2001 From: Code Sky Date: Sat, 18 Jul 2026 12:05:35 +0000 Subject: [PATCH] fix: surface silently swallowed errors across storage, physics, and AI paths Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- src/features/ai/AiAgentPanel.tsx | 24 +++++++++------- src/features/ai/agentTools.ts | 4 +-- src/features/auth/AuthProvider.tsx | 6 ++-- src/features/recording/RecordingExporter.ts | 28 +++++++++++++++---- src/features/sandbox/SandboxJoints.tsx | 6 +++- src/features/sandbox/sceneStorage.ts | 3 +- src/features/settings/physicsSettingsStore.ts | 9 ++++-- 7 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/features/ai/AiAgentPanel.tsx b/src/features/ai/AiAgentPanel.tsx index 8e6cc7e..243b14b 100644 --- a/src/features/ai/AiAgentPanel.tsx +++ b/src/features/ai/AiAgentPanel.tsx @@ -80,7 +80,8 @@ export function AiAgentPanel({ onOpenSettings }: AiAgentPanelProps) { const parsed = JSON.parse(raw) if (!Array.isArray(parsed)) return [] return parsed.slice(-MAX_MESSAGES) - } catch { + } catch (err) { + console.warn('[AiAgentPanel] failed to load stored messages, starting fresh:', err) return [] } } @@ -89,16 +90,16 @@ export function AiAgentPanel({ onOpenSettings }: AiAgentPanelProps) { try { const trimmed = msgs.length > MAX_MESSAGES ? msgs.slice(-MAX_MESSAGES) : msgs localStorage.setItem(STORAGE_KEY, JSON.stringify(trimmed)) - } catch { - // localStorage full or unavailable + } catch (err) { + console.warn('[AiAgentPanel] failed to persist messages (storage full or unavailable):', err) } } function clearStoredMessages() { try { localStorage.removeItem(STORAGE_KEY) - } catch { - // ignore + } catch (err) { + console.warn('[AiAgentPanel] failed to clear stored messages:', err) } } @@ -121,7 +122,10 @@ export function AiAgentPanel({ onOpenSettings }: AiAgentPanelProps) { if (!user) return fetchAiConfig() .then(({ config: cfg }) => setConfig(cfg)) - .catch(() => setConfig(null)) + .catch((err) => { + console.debug('[AiAgentPanel] failed to load AI config:', err) + setConfig(null) + }) }, [user]) useEffect(() => { @@ -207,8 +211,8 @@ export function AiAgentPanel({ onOpenSettings }: AiAgentPanelProps) { } } } - } catch { - // ignore malformed lines + } catch (err) { + console.debug('[AiAgentPanel] skipping malformed SSE line:', trimmed, err) } } return { content, toolCalls: Object.values(toolCallsRef.current) } @@ -222,8 +226,8 @@ export function AiAgentPanel({ onOpenSettings }: AiAgentPanelProps) { let args: Record = {} try { args = JSON.parse(call.arguments) - } catch { - // ignore + } catch (err) { + console.warn(`[AiAgentPanel] failed to parse arguments for tool "${call.name}":`, err) } const result = await executeTool(call.name, args, toolContext) call.status = result.success ? 'success' : 'error' diff --git a/src/features/ai/agentTools.ts b/src/features/ai/agentTools.ts index bf5b29b..f764a57 100644 --- a/src/features/ai/agentTools.ts +++ b/src/features/ai/agentTools.ts @@ -284,8 +284,8 @@ function parseVector(raw: string | unknown[] | undefined, defaultValue: number[] if (Array.isArray(parsed) && parsed.every((v) => typeof v === 'number')) { return parsed } - } catch { - // ignore + } catch (err) { + console.debug('[agentTools] failed to parse vector argument, using default:', raw, err) } } return defaultValue diff --git a/src/features/auth/AuthProvider.tsx b/src/features/auth/AuthProvider.tsx index c3ee3c1..fc63b7a 100644 --- a/src/features/auth/AuthProvider.tsx +++ b/src/features/auth/AuthProvider.tsx @@ -16,7 +16,8 @@ function getStoredUser(): User | null { try { const raw = window.localStorage.getItem(USER_KEY) return raw ? (JSON.parse(raw) as User) : null - } catch { + } catch (err) { + console.warn('[AuthProvider] failed to parse stored user, ignoring it:', err) return null } } @@ -40,8 +41,9 @@ export function AuthProvider({ children }: { children: ReactNode }) { setUser(currentUser) window.localStorage.setItem(USER_KEY, JSON.stringify(currentUser)) }) - .catch(() => { + .catch((err) => { if (cancelled) return + console.warn('[AuthProvider] session validation failed, signing out:', err) window.localStorage.removeItem(TOKEN_KEY) window.localStorage.removeItem(USER_KEY) setToken(null) diff --git a/src/features/recording/RecordingExporter.ts b/src/features/recording/RecordingExporter.ts index da401a4..b5bcd56 100644 --- a/src/features/recording/RecordingExporter.ts +++ b/src/features/recording/RecordingExporter.ts @@ -7,21 +7,37 @@ import type { RecordingState } from '@/features/sandbox/sandboxStore' */ export function exportRecordingAsWebM(recording: RecordingState): void { const { frames } = recording - if (frames.length === 0) return + if (frames.length === 0) { + console.warn('[RecordingExporter] no frames to export as WebM') + return + } const canvas = document.createElement('canvas') canvas.width = 1280 canvas.height = 720 const ctx = canvas.getContext('2d') - if (!ctx) return + if (!ctx) { + console.error('[RecordingExporter] could not obtain 2D canvas context; WebM export aborted') + return + } const stream = canvas.captureStream(30) const chunks: Blob[] = [] - const mediaRecorder = new MediaRecorder(stream, { - mimeType: 'video/webm;codecs=vp9', - videoBitsPerSecond: 5000000, - }) + let mediaRecorder: MediaRecorder + try { + mediaRecorder = new MediaRecorder(stream, { + mimeType: 'video/webm;codecs=vp9', + videoBitsPerSecond: 5000000, + }) + } catch (err) { + console.error('[RecordingExporter] MediaRecorder is not supported for this format:', err) + return + } + + mediaRecorder.onerror = (e) => { + console.error('[RecordingExporter] MediaRecorder error while capturing WebM:', e) + } mediaRecorder.ondataavailable = (e) => { if (e.data.size > 0) { diff --git a/src/features/sandbox/SandboxJoints.tsx b/src/features/sandbox/SandboxJoints.tsx index ba69c58..03fdbbf 100644 --- a/src/features/sandbox/SandboxJoints.tsx +++ b/src/features/sandbox/SandboxJoints.tsx @@ -84,7 +84,11 @@ function createJoint( default: return null } - } catch { + } catch (err) { + console.warn( + `[SandboxJoints] failed to create "${joint.type}" joint (${joint.id}) between ${joint.bodyA} and ${joint.bodyB}:`, + err + ) return null } } diff --git a/src/features/sandbox/sceneStorage.ts b/src/features/sandbox/sceneStorage.ts index 1cfef44..f40f833 100644 --- a/src/features/sandbox/sceneStorage.ts +++ b/src/features/sandbox/sceneStorage.ts @@ -146,7 +146,8 @@ export function loadStoredScene(): SandboxScene | null { try { const parsed = JSON.parse(raw) as unknown return migrateScene(parsed) - } catch { + } catch (err) { + console.warn('[sceneStorage] failed to load stored scene, discarding it:', err) return null } } diff --git a/src/features/settings/physicsSettingsStore.ts b/src/features/settings/physicsSettingsStore.ts index 0a4ce2d..8a43e15 100644 --- a/src/features/settings/physicsSettingsStore.ts +++ b/src/features/settings/physicsSettingsStore.ts @@ -38,7 +38,8 @@ function loadStoredSettings(): Partial { if (!raw) return {} const parsed = JSON.parse(raw) as Partial return parsed - } catch { + } catch (err) { + console.warn('[physicsSettingsStore] failed to parse stored settings, using defaults:', err) return {} } } @@ -101,5 +102,9 @@ export const usePhysicsSettingsStore = create((set, get) = usePhysicsSettingsStore.subscribe((state) => { if (typeof window === 'undefined') return - window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state)) + try { + window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state)) + } catch (err) { + console.warn('[physicsSettingsStore] failed to persist settings:', err) + } })