Skip to content

Commit b05afe6

Browse files
committed
fix: address message queueing issues
- Fix race condition in queue processing by re-checking queue state inside setTimeout - Add error handling for queue operations with retry mechanism - Replace array index with stable message.id for React keys in QueuedMessages - Generate more unique IDs using timestamp + random component
1 parent 5ad31d1 commit b05afe6

File tree

2 files changed

+79
-44
lines changed

2 files changed

+79
-44
lines changed

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 78 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -550,49 +550,66 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
550550
*/
551551
const handleSendMessage = useCallback(
552552
(text: string, images: string[], fromQueue = false) => {
553-
text = text.trim()
554-
555-
if (text || images.length > 0) {
556-
if (sendingDisabled && !fromQueue) {
557-
setMessageQueue((prev) => [...prev, { id: Date.now().toString(), text, images }])
558-
setInputValue("")
559-
setSelectedImages([])
560-
return
561-
}
562-
// Mark that user has responded - this prevents any pending auto-approvals
563-
userRespondedRef.current = true
564-
565-
if (messagesRef.current.length === 0) {
566-
vscode.postMessage({ type: "newTask", text, images })
567-
} else if (clineAskRef.current) {
568-
if (clineAskRef.current === "followup") {
569-
markFollowUpAsAnswered()
553+
try {
554+
text = text.trim()
555+
556+
if (text || images.length > 0) {
557+
if (sendingDisabled && !fromQueue) {
558+
// Generate a more unique ID using timestamp + random component
559+
const messageId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
560+
setMessageQueue((prev) => [...prev, { id: messageId, text, images }])
561+
setInputValue("")
562+
setSelectedImages([])
563+
return
570564
}
565+
// Mark that user has responded - this prevents any pending auto-approvals
566+
userRespondedRef.current = true
567+
568+
if (messagesRef.current.length === 0) {
569+
vscode.postMessage({ type: "newTask", text, images })
570+
} else if (clineAskRef.current) {
571+
if (clineAskRef.current === "followup") {
572+
markFollowUpAsAnswered()
573+
}
571574

572-
// Use clineAskRef.current
573-
switch (
574-
clineAskRef.current // Use clineAskRef.current
575-
) {
576-
case "followup":
577-
case "tool":
578-
case "browser_action_launch":
579-
case "command": // User can provide feedback to a tool or command use.
580-
case "command_output": // User can send input to command stdin.
581-
case "use_mcp_server":
582-
case "completion_result": // If this happens then the user has feedback for the completion result.
583-
case "resume_task":
584-
case "resume_completed_task":
585-
case "mistake_limit_reached":
586-
vscode.postMessage({ type: "askResponse", askResponse: "messageResponse", text, images })
587-
break
588-
// There is no other case that a textfield should be enabled.
575+
// Use clineAskRef.current
576+
switch (
577+
clineAskRef.current // Use clineAskRef.current
578+
) {
579+
case "followup":
580+
case "tool":
581+
case "browser_action_launch":
582+
case "command": // User can provide feedback to a tool or command use.
583+
case "command_output": // User can send input to command stdin.
584+
case "use_mcp_server":
585+
case "completion_result": // If this happens then the user has feedback for the completion result.
586+
case "resume_task":
587+
case "resume_completed_task":
588+
case "mistake_limit_reached":
589+
vscode.postMessage({
590+
type: "askResponse",
591+
askResponse: "messageResponse",
592+
text,
593+
images,
594+
})
595+
break
596+
// There is no other case that a textfield should be enabled.
597+
}
598+
} else {
599+
// This is a new message in an ongoing task.
600+
vscode.postMessage({ type: "askResponse", askResponse: "messageResponse", text, images })
589601
}
590-
} else {
591-
// This is a new message in an ongoing task.
592-
vscode.postMessage({ type: "askResponse", askResponse: "messageResponse", text, images })
593-
}
594602

595-
handleChatReset()
603+
handleChatReset()
604+
}
605+
} catch (error) {
606+
console.error("Error in handleSendMessage:", error)
607+
// If this was a queued message, we should handle it differently
608+
if (fromQueue) {
609+
throw error // Re-throw to be caught by the queue processor
610+
}
611+
// For direct sends, we could show an error to the user
612+
// but for now we'll just log it
596613
}
597614
},
598615
[handleChatReset, markFollowUpAsAnswered, sendingDisabled], // messagesRef and clineAskRef are stable
@@ -601,13 +618,31 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
601618
useEffect(() => {
602619
if (!sendingDisabled && messageQueue.length > 0 && !isProcessingQueueRef.current) {
603620
isProcessingQueueRef.current = true
604-
const nextMessage = messageQueue[0]
605621

606622
// Use setTimeout to ensure state updates are processed
607623
setTimeout(() => {
608-
handleSendMessage(nextMessage.text, nextMessage.images, true)
609-
setMessageQueue((prev) => prev.slice(1))
610-
isProcessingQueueRef.current = false
624+
// Re-check the queue inside the timeout to avoid race conditions
625+
setMessageQueue((prev) => {
626+
if (prev.length > 0) {
627+
const [nextMessage, ...remaining] = prev
628+
// Process the message asynchronously to avoid blocking
629+
Promise.resolve()
630+
.then(() => {
631+
handleSendMessage(nextMessage.text, nextMessage.images, true)
632+
})
633+
.catch((error) => {
634+
console.error("Failed to send queued message:", error)
635+
// Re-add the message to the queue on error
636+
setMessageQueue((current) => [nextMessage, ...current])
637+
})
638+
.finally(() => {
639+
isProcessingQueueRef.current = false
640+
})
641+
return remaining
642+
}
643+
isProcessingQueueRef.current = false
644+
return prev
645+
})
611646
}, 0)
612647
}
613648
}, [sendingDisabled, messageQueue, handleSendMessage])

webview-ui/src/components/chat/QueuedMessages.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const QueuedMessages: React.FC<QueuedMessagesProps> = ({ queue, onRemove }) => {
2323
<div className="flex flex-col gap-2">
2424
{queue.map((message, index) => (
2525
<div
26-
key={index}
26+
key={message.id}
2727
className="bg-vscode-editor-background border rounded-xs p-1 overflow-hidden whitespace-pre-wrap">
2828
<div className="flex justify-between">
2929
<div className="flex-grow px-2 py-1 wrap-anywhere">

0 commit comments

Comments
 (0)