From 27377efaf32236fa222a9e372f45b3f4f01144ac Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 26 Aug 2026 17:10:05 +0300 Subject: [PATCH] fix(chat): return the typed prompt to the input on any send failure The failure handler restored the text only for a new-session draft; a regular session kept its attachments but lost the prompt to a toast. The restore now runs before any cause-specific branching: an unchanged composer gets the text (and the session draft) back, new typing gets the failed prompt appended instead of clobbered, and a mid-send session switch writes it into the originating session's persisted draft. --- packages/ui/src/components/chat/ChatInput.tsx | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index 2cf2578b..f028ecd1 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -1383,10 +1383,25 @@ const ChatInputComponent: React.FC = ({ console.error('Message send failed:', rawMessage || error); restoreConsumedDrafts(); - const currentInput = composerRef.current?.getValue() ?? messageRef.current; - if (newSessionDraftOpen && inputSnapshot.message && (!currentInput || currentInput === inputSnapshot.message)) { - setMessage(inputSnapshot.message); - writeChatDraft(chatDraftIdentity, inputSnapshot.message, confirmedMentionsRef.current); + // A failed send returns the typed prompt no matter WHY it failed — + // auth, network, server, anything. Losing a long prompt to a toast + // is the one outcome this handler must never produce. + if (inputSnapshot.message) { + if (currentChatDraftIdentityRef.current !== chatDraftIdentity) { + // The user switched sessions mid-send: restore into that + // session's persisted draft, not the visible composer. + writeChatDraft(chatDraftIdentity, inputSnapshot.message, confirmedMentionsRef.current); + } else { + const currentInput = composerRef.current?.getValue() ?? messageRef.current; + if (!currentInput || currentInput === inputSnapshot.message) { + setMessage(inputSnapshot.message); + writeChatDraft(chatDraftIdentity, inputSnapshot.message, confirmedMentionsRef.current); + } else { + // New typing already lives in the composer; the failed + // prompt joins it instead of clobbering either text. + useInputStore.getState().setPendingInputText(inputSnapshot.message, 'append'); + } + } } const isSoftNetworkError =