From 762b999d1e6ecc0e4e3119752f7e863f4d026022 Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Mon, 17 Aug 2026 22:19:52 +0200 Subject: [PATCH] fix(agent): key chat message parts by index Every step-start part serialises to the same JSON, so the content-derived key collided for any multi-step assistant message and React warned about duplicate keys on each incoming chunk. Two identical text parts collided the same way. Parts are append-only and never reordered by the AI SDK, so the index is stable. --- .../src/routes/agent/-components/agent-chat.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/web/src/routes/agent/-components/agent-chat.tsx b/apps/web/src/routes/agent/-components/agent-chat.tsx index 52ab33031..dc0596248 100644 --- a/apps/web/src/routes/agent/-components/agent-chat.tsx +++ b/apps/web/src/routes/agent/-components/agent-chat.tsx @@ -373,13 +373,9 @@ function StarterPromptMarquee({ onSelect }: StarterPromptMarqueeProps) { ); } -function getMessagePartKey(messageId: string, part: UIMessage["parts"][number]) { - if ("toolCallId" in part && typeof part.toolCallId === "string") - return `${messageId}-${part.type}-${part.toolCallId}`; - if (part.type === "text") return `${messageId}-text-${part.text}`; - if (part.type === "file") return `${messageId}-file-${part.url ?? part.filename}`; - return `${messageId}-${part.type}-${JSON.stringify(part)}`; -} +// ponytail: parts are append-only and never reordered by the AI SDK, so the index is a stable +// unique key. Content-derived keys collide — every `step-start` part serializes identically. +const getMessagePartKey = (messageId: string, index: number) => `${messageId}-${index}`; export function AssistantMarkdown({ text }: AssistantMarkdownProps) { return ( @@ -544,9 +540,9 @@ function ChatMessage({ message, onAnswer, onRevert, isReverting, actionsById }: return ( - {message.parts.map((part) => ( + {message.parts.map((part, index) => (