bug(chat): #827 fix introduced regression — move chat fails with 'User is missing' (auth module not namespaced)
Summary
After the #827 (closed) fix (b6fe6fc68), moving a chat to a folder now genuinely fails (the move no longer happens at all). The #827 (closed) fix converted a false error (move succeeded but error toast showed) into a real failure (move is blocked).
Client error:
chatHistoryStore.js:202 Error moving chat 17815264412669961: Error: User is missing
at chatHistoryStore.js:195:33
...
at te.moveChat (chatHistoryStore.js:192:83)
Reproduce
- Log in (user is authenticated, can see chats/folders)
- Right-click a conversation → Move to folder → select a folder
- Error toast: "Failed to move conversation"
- Refresh — conversation is NOT in the folder (move genuinely did not happen)
Confirmed on both local build (C:\Dev\builds\main, image c7aae0c2) and cloud (10.0.0.101, image c7aae0c2). Backend logs show no POST /api/chat/conversations/:id/move request — the failure is purely client-side, the request never leaves the browser.
Root Cause
The auth Vuex module is NOT namespaced, but chatHistoryStore.moveChat accesses its getter with the namespaced path rootGetters['auth/currentUser'], which returns undefined.
Evidence
-
components/gov-chat-frontend/src/store/modules/auth.js— has NOnamespaced: truedeclaration:const state = { ... user: null ... }; const getters = { currentUser: (state) => state.user, // registered GLOBALLY ... }; // exported without namespaced: true -
components/gov-chat-frontend/src/store/index.js— registersauthas a plain module:modules: { chatHistory: chatHistoryStore, auth } // auth not namespaced -
Because
authis not namespaced, its getters live in the global namespace. Every other component accesses it globally (proof):-
App.vue:79—...mapGetters(['isAuthenticated', 'currentUser']) -
AdminDashboard.vue:2449—this.$store.getters.currentUser -
ChatBotComponent.vue:530,1141,1224—this.$store.getters.currentUser -
ChatFolders.vue:553,677—this.$store.getters.currentUser -
NavBarComponent.vue:317,SettingsComponent.vue:434,455—this.$store.getters.currentUser
-
-
ONLY
chatHistoryStore.js:193uses the namespaced form:const currentUser = rootGetters['auth/currentUser']; // returns undefined if (!currentUser) throw new Error('User is missing'); // always throws
Why #827 (closed) Turned This From False-Error Into Real-Failure
Before #827 (closed) (ChatFolders.handleMoveChat):
await chatHistoryService.moveConversation(...) // 1. direct API call — SUCCEEDS
await this.moveChat(...) // 2. store action — currentUser check throws
// → false error toast, but move already done
The component called the move API directly first, so the move completed before the store action's broken auth/currentUser check threw. The user saw an error, but the move worked (the original #827 (closed) symptom).
After #827 (closed) (b6fe6fc68 removed the duplicate direct call):
await this.moveChat(...) // ONLY the store action runs
// → currentUser check throws FIRST
// → moveConversation() never called
// → move genuinely fails
Now only the store action runs, so the broken check blocks the move entirely. The #827 (closed) regression test did not catch this because it mocks rootGetters directly rather than exercising the real module registration.
Suggested Fix (one of)
-
Match the global namespace (smallest change) —
chatHistoryStore.js:193:const currentUser = rootGetters['currentUser']; // drop the 'auth/' prefix -
Remove the dead guard — the
currentUservalue is checked but never used afterward (moveConversationonly takeschatId,fromFolderId,toFolderId). The guard adds no value and is the source of the bug:async moveChat({ commit, rootGetters }, { chatId, fromFolderId, toFolderId }) { // remove the currentUser check entirely — moveConversation doesn't need it await chatHistoryService.moveConversation(chatId, fromFolderId, toFolderId); ... } -
Namespace the auth module (largest change, NOT recommended alone) — add
namespaced: truetomodules/auth.js, then update ALL the global accesses (getters.currentUser→getters['auth/currentUser'],mapGetters(['currentUser'])→mapGetters('auth', ['currentUser']), etc.) across App.vue, AdminDashboard.vue, ChatBotComponent.vue, ChatFolders.vue, NavBarComponent.vue, SettingsComponent.vue. High blast radius — prefer option 1 or 2.
Recommended: option 1 (one-line, matches existing convention used everywhere else in the codebase).
Files
-
components/gov-chat-frontend/src/store/chatHistoryStore.js:192-195— the brokenrootGetters['auth/currentUser']access -
components/gov-chat-frontend/src/store/modules/auth.js— missingnamespaced: true(root structural cause) -
components/gov-chat-frontend/src/store/index.js— module registration
Regression Test Gap
The #827 (closed) test (src/__tests__/store/chatHistory.test.js) mocks rootGetters as a plain object, so rootGetters['auth/currentUser'] resolves to the mocked value and never exercises the real Vuex module namespace resolution. A follow-up test should dispatch moveChat against a real store with the actual auth module registered (un-namespaced) to catch this class of bug.
Environment
- Commit:
c7aae0c22(both local build and cloud 10.0.0.101) - Introduced by:
b6fe6fc68(fix #827 (closed)) - Browser: Chrome 149
Related
- #827 (closed) — the original false-error issue; its fix introduced this regression