Problem
When sending a chat message fails (network offline, server unreachable, or a non-2xx response), the message silently disappears. The input is cleared, the optimistic user message flashes and is removed, no error is shown, and the text is gone. The only trace is an unhandled TypeError: Failed to fetch in the console.
Steps to reproduce
- Open a chat and type a message (optionally attach a file).
- Make the request fail, e.g. DevTools → Network → Offline, or stop the server.
- Press Enter.
Actual: the message appears for a moment, then vanishes; the input and attachments are empty; no toast. For a new chat the tab title keeps the first 40 characters of the lost message.
Expected: an error is shown and the message (and attachments) stay in the input so it can be re-sent.
Root cause
In ChatPanel.svelte send():
inputText = '' and chatInputEl.clearUploads() run before apiSendMessage is awaited.
- The
catch block only removes the optimistic message and rethrows. Nothing restores the draft and no toast is shown.
ChatInput calls onsend() without awaiting, so the rethrown error becomes an unhandled rejection and never reaches the user.
- The streaming/queue branch has the same problem (its
catch only logs).
Proposed fix
- Capture the untransformed draft text and the attached files before sending.
- On failure, restore both, reset
currentMessageId, and reset the new-chat tab label.
- Replace
throw e with toast.error(...), matching how compact/fork failures are reported. ApiError messages from the server are shown as-is; other failures (network) use a new chat.sendFailed string.
- Apply the same restoration in the queue branch.
ChatInput gets a small restoreUploads() counterpart to clearUploads().
Deliberately minimal: no retry button or "failed" marker on the message; happy to adjust if you prefer a different UX.
Verification
Playwright (headless Chromium) against a local build, aborting POST /api/chats with internetdisconnected:
|
before |
after |
| input after failed send |
empty |
text restored |
| toast |
none |
"Failed to send message" |
| new-chat tab title |
first 40 chars of the lost message |
"New Chat" |
| unhandled rejection |
yes |
no |
Additional context
Implemented on a fork (4 files, +20/-3):
main...ebifrier:computer:fix/keep-message-on-send-failure
Related but different cause: #262 (prompt deleted when moving the window) does not involve sending.
Problem
When sending a chat message fails (network offline, server unreachable, or a non-2xx response), the message silently disappears. The input is cleared, the optimistic user message flashes and is removed, no error is shown, and the text is gone. The only trace is an unhandled
TypeError: Failed to fetchin the console.Steps to reproduce
Actual: the message appears for a moment, then vanishes; the input and attachments are empty; no toast. For a new chat the tab title keeps the first 40 characters of the lost message.
Expected: an error is shown and the message (and attachments) stay in the input so it can be re-sent.
Root cause
In
ChatPanel.sveltesend():inputText = ''andchatInputEl.clearUploads()run beforeapiSendMessageis awaited.catchblock only removes the optimistic message and rethrows. Nothing restores the draft and no toast is shown.ChatInputcallsonsend()without awaiting, so the rethrown error becomes an unhandled rejection and never reaches the user.catchonly logs).Proposed fix
currentMessageId, and reset the new-chat tab label.throw ewithtoast.error(...), matching how compact/fork failures are reported.ApiErrormessages from the server are shown as-is; other failures (network) use a newchat.sendFailedstring.ChatInputgets a smallrestoreUploads()counterpart toclearUploads().Deliberately minimal: no retry button or "failed" marker on the message; happy to adjust if you prefer a different UX.
Verification
Playwright (headless Chromium) against a local build, aborting
POST /api/chatswithinternetdisconnected:Additional context
Implemented on a fork (4 files, +20/-3):
main...ebifrier:computer:fix/keep-message-on-send-failure
Related but different cause: #262 (prompt deleted when moving the window) does not involve sending.