From e8d01b6a780b5bc1d00f8a81dff9d0c92ee4b3ea Mon Sep 17 00:00:00 2001 From: iajibose Date: Thu, 30 Jul 2026 12:19:04 +0100 Subject: [PATCH 1/2] Trim raw localStorage value before parsing wallet session Small first step toward #1082. Full try/catch error boundary around hydrate, clearing invalid stored sessions, and the regression test still need to be added in a follow-up. --- frontend/src/context/wallet-context.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/context/wallet-context.tsx b/frontend/src/context/wallet-context.tsx index 3538a7c9..43d9f4d9 100644 --- a/frontend/src/context/wallet-context.tsx +++ b/frontend/src/context/wallet-context.tsx @@ -142,7 +142,7 @@ function readStoredSession(): WalletSession | null { return null; } - const raw = window.localStorage.getItem(STORAGE_KEY); + const raw = window.localStorage.getItem(STORAGE_KEY)?.trim(); if (!raw) { return null; } From 545ea3fa1c3521d62c8fcc006832cd9be139ad9e Mon Sep 17 00:00:00 2001 From: Ajibose Date: Sun, 2 Aug 2026 19:21:39 +0300 Subject: [PATCH 2/2] Add regression test for syntactically invalid wallet session storage The existing "malformed session" test only covered valid JSON with an unexpected shape. This adds a case with unparsable JSON to exercise the JSON.parse catch path in readStoredSession, confirming hydrate falls back to a clean disconnected state and clears the bad key. Co-Authored-By: Claude Sonnet 5 --- frontend/src/context/wallet-context.test.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/frontend/src/context/wallet-context.test.tsx b/frontend/src/context/wallet-context.test.tsx index 7a1dad1d..777f0b2e 100644 --- a/frontend/src/context/wallet-context.test.tsx +++ b/frontend/src/context/wallet-context.test.tsx @@ -77,6 +77,23 @@ describe('WalletProvider', () => { expect(result.current.session).toBeNull(); }); + it('should recover from a syntactically invalid stored session without crashing', async () => { + localStorage.setItem(STORAGE_KEY, '{not valid json,,,'); + + const { result } = renderHook(() => useWallet(), { + wrapper: createWrapper(), + }); + + await waitFor(() => { + expect(result.current.isHydrated).toBe(true); + }); + + expect(result.current.status).toBe('idle'); + expect(result.current.session).toBeNull(); + expect(result.current.errorMessage).toBeNull(); + expect(localStorage.getItem(STORAGE_KEY)).toBeNull(); + }); + it('should discard a session with mocked !== false', async () => { const mockedSession = { ...mockSession, mocked: true }; localStorage.setItem(STORAGE_KEY, JSON.stringify(mockedSession));