← Back to PRs

#8344: fix: persist webchat message queue to localStorage across browser refresh

by vishaltandale00 open 2026-02-03 22:14 View on GitHub →
app: web-ui stale
Fixes #8331 - Chat message queue lost on browser refresh ## Problem Messages queued while the agent is busy (the "X pending" queue) are lost when the user refreshes the browser. ### Root Cause The message queue exists only in React/Lit state. No persistence layer. ## Solution Added localStorage persistence for the chat message queue: - **Queue persistence**: Queue items are saved to localStorage whenever modified (enqueue, dequeue, remove) - **Session-scoped**: Storage is keyed by `sessionKey` to prevent conflicts between sessions - **Auto-restore**: Queue is automatically restored from localStorage on chat refresh/reconnect - **Stale filtering**: Messages older than 24 hours are automatically filtered out - **Graceful degradation**: Failed localStorage operations are handled with warnings ## Implementation ### Added Functions - `saveQueueToStorage()`: Persists queue to localStorage - `loadQueueFromStorage()`: Loads and filters stale messages - `restoreQueueFromStorage()`: Public API to restore queue on reconnect ### Modified Functions - `enqueueChatMessage()`: Now saves queue after adding items - `flushChatQueue()`: Saves queue after dequeuing items - `removeQueuedMessage()`: Saves queue after removing items - `refreshChat()`: Restores queue on load and attempts flush if agent is idle ## Benefits ✅ Users no longer lose pending messages on browser refresh ✅ Queue persists across page reloads during long-running agent operations ✅ Automatic cleanup prevents stale messages from accumulating ✅ Session-specific storage prevents queue conflicts ✅ Handles localStorage quota/availability gracefully ## Testing Tested manually: 1. Send message that triggers long operation 2. Queue additional messages while agent is busy 3. Refresh browser before agent finishes 4. ✅ Queue is restored and messages are sent when agent completes 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This change adds localStorage persistence for the webchat “pending messages” queue by saving the queue on enqueue/dequeue/remove and restoring it during `refreshChat()`. It keys storage by `sessionKey` and filters out entries older than 24 hours, so queued messages survive browser refreshes during long agent runs. Main thing to double-check is restore semantics and input robustness: `restoreQueueFromStorage()` currently won’t clear an existing in-memory queue when storage is empty, and the restored JSON isn’t validated beyond `Array.isArray`, which can allow corrupted/outdated stored values to propagate into runtime logic. <h3>Confidence Score: 4/5</h3> - This PR is generally safe to merge; the persistence logic is straightforward with minor edge-case risks around restore semantics and corrupted storage data. - Changes are localized to queue management and guarded by try/catch around localStorage access. The main concerns are (1) restore not clearing existing in-memory state when storage is empty, which can cause stale UI state in reconnect/hot-reload flows, and (2) lack of per-item validation when restoring parsed JSON. - ui/src/ui/app-chat.ts <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs