bug(chat): move chat to folder shows client error despite successful backend operation
Summary
Moving a chat to a folder shows an error notification on the frontend, but the move operation actually succeeds on the backend (HTTP 200, ArangoDB edge created correctly).
Steps to Reproduce
- Create a new conversation
- Create a folder (e.g., "Business")
- Right-click conversation → Move to folder → select the folder
- Frontend shows error toast: "Failed to move conversation"
- Refresh the page — conversation IS inside the folder (move actually worked)
Root Cause Analysis
Backend (WORKING)
The POST /api/chat/conversations/:id/move endpoint works correctly:
- Returns
{ success: true, conversationId, sourceFolderId, targetFolderId } - Creates the
folderConversationsedge in ArangoDB (folders/X→conversations/Y) - Confirmed via ArangoDB: edge exists with correct
_from,_to,addedAt,addedBy
Frontend (BUG — double API call + cascading failure)
The frontend calls the move API twice and has a fragile follow-up call:
ChatFolders.vue:handleMoveChat() (line 1194):
// 1st API call — succeeds
await chatHistoryService.moveConversation(this.activeChat._key, this.selectedFolderId, this.destinationFolderId);
// 2nd API call — calls moveConversation AGAIN inside store action
await this.moveChat({ chatId: this.activeChat._key, fromFolderId: this.selectedFolderId, toFolderId: this.destinationFolderId });
chatHistoryStore.js:moveChat() (line 192):
async moveChat({ commit, rootGetters }, { chatId, fromFolderId, toFolderId }) {
await chatHistoryService.moveConversation(chatId, fromFolderId, toFolderId); // 2nd redundant API call
const folder = await chatHistoryService.getFolder(toFolderId); // 3rd API call — can fail
const chatIds = folder.conversations.map((conv) => conv._key); // TypeError if conversations is undefined
commit('SET_FOLDER_CHATS', { folderId: toFolderId, chats: chatIds });
commit('MOVE_CHAT', { chatId, fromFolderId, toFolderId });
}
Failure Scenarios
-
Double move: The component calls
moveConversation(), then the store action calls it again. On the second call, the backend returns{ success: true, noChangesNeeded: true }because the edge already exists. This is harmless but wasteful. -
getFolder()after move: The store action then fetches the folder withgetFolder(toFolderId). If this fails (network error, race condition, or the folder conversations query returns unexpected data), the entiremoveChataction throws, the error propagates tohandleMoveChat(), and the catch block shows the error toast — even though the move already succeeded. -
folder.conversations.map(): IfgetFolder()returns a response whereconversationsisundefinedornull(edge case), this line throws aTypeErrorthat gets caught as a move failure.
Evidence
Backend logs show the move succeeds:
Conversation 17812734745915798 moved from root to folder 1781273482462
POST /api/chat/conversations/17812734745915798/move 200 17.223 ms
ArangoDB edge confirmed:
{
"_from": "folders/1781273482462",
"_to": "conversations/17812734745915798",
"addedAt": "2026-06-12T14:11:33.272Z"
}
Files Involved
-
components/gov-chat-frontend/src/components/ChatFolders.vue—handleMoveChat()method (line 1171) -
components/gov-chat-frontend/src/store/chatHistoryStore.js—moveChataction (line 192) -
components/gov-chat-frontend/src/services/chatHistoryService.js—moveConversation()(line 320),getFolder()(line 202) -
components/gov-chat-backend/services/chat-history-service.js—moveConversation()(line 2077),getFolder()(line 1483)
Suggested Fix
-
Remove the duplicate API call:
ChatFolders.vue:handleMoveChat()should either call the API directly OR use the store action, not both. Remove the directchatHistoryService.moveConversation()call at line 1194 and rely solely on the store action, or vice versa. -
Add null safety in the store action: Guard
folder.conversationsbefore calling.map():const chatIds = (folder.conversations || []).map((conv) => conv._key); -
Separate concerns: The store action should not re-call the API if the component already did. Either the component calls the API and dispatches a local-only mutation to update state, or the component only dispatches the store action (which handles the API call).
Environment
- Local build:
C:\Dev\builds\mainon Docker Desktop WSL2 - Commit:
39fe871(latest main) - Remote GPU:
ai.assembly.govstack.global - Browser: Chrome 149