Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

withPersistence re-appends stored tool results on an interrupt resume (snapshot ids never match stored id-less tool messages)

Đã đóng
#1,580 1 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@tombeckenham đang làm issue này rồi.

Từ ngày 30/9/2026.

  • #1605 của @AlemTuzlak — đang mở

Đánh giá

Issue này chưa được đánh giá.

Mô tả

has-pr waiting-on: maintainer

Summary

Resuming a run that parked on a client-tool interrupt, when that run made a server tool call first, saves the tool result twice. withPersistence appends a second copy of every earlier tool result after the interrupted assistant turn. The next provider request is then invalid. Anthropic answers 400 invalid_request_error: messages.N.content.0: unexpected tool_use_id found in tool_result blocks … Each tool_result block must have a corresponding tool_use block in the previous message. The thread stays broken for every later run.

Versions: @tanstack/ai 0.61.0, @tanstack/ai-persistence 0.6.7, @tanstack/ai-client 0.35.1.

Mechanism

  1. The run calls a server tool (result stored as { role: "tool", toolCallId: "call-3" }, no id). It then calls a client tool and stops on the interrupt.
  2. On the interrupt, buildMessagesSnapshotChunk (activities/chat/index.js) gives id-less model messages a fresh id: snapshot_${runId}_${index}. The browser's StreamProcessor takes the MESSAGES_SNAPSHOT as its state.
  3. The user answers. ChatClient posts uiMessagesToWire(messages), where the stored tool result now carries id: "snapshot_<run>_2".
  4. mergeStoredMessages(stored, incoming) (ai-persistence/src/merge-stored.ts):
    • The cutoff is the last incoming id found in the store: the interrupted assistant turn.
    • The incoming tool message has id snapshot_…_2, which the store has never seen (its copy has no id). So it is pushed after the prefix: a second call-3 result, right after the assistant turn that called only the client tool.
    • Id-less incoming tool messages fall through to the existing.content === message.content check. It never matches when content is a parts array (an MCP result, for example), so those get re-appended too.

Repro (shape)

Server: chat({ messages, middleware: [withPersistence(store)] }) with a scripted adapter:

  1. step 1: call server tool load_skill.
  2. step 2: call client tool ask_user (the run interrupts).
  3. step 3: text.

Browser side, as ChatClient does it:

const processor = new StreamProcessor();
for (const chunk of chunksOfFirstRun) processor.processChunk(chunk); // includes MESSAGES_SNAPSHOT
processor.addToolResult(askUserCallId, answer);
const messages = uiMessagesToWire(processor.getMessages());
// POST { messages, resume: [{ interruptId: `client_tool_${askUserCallId}`, status: "resolved", payload: answer }] }

What the server sees:

posted: [user message-1, assistant message-2, tool(id "snapshot_agui-run-1_2", call-3), assistant message-5, tool(id "tool-call-6", call-6)]
stored: [user message-1, assistant message-2, tool(no id, call-3),               assistant message-5]
merged → tool results in order: call-3, call-3, call-6   (expected: call-3, call-6)

Expected

A tool result already stored for a toolCallId is not appended again, whatever id the client gives it. Possible fixes:

  • give stored model messages the same ids the snapshot uses;
  • or have mergeStoredMessages match tool messages by toolCallId instead of id / content identity.

Workaround

Before chat(), drop posted role: "tool" messages whose toolCallId the stored thread already answers.

Ngôn ngữ chính
TypeScript
Star
3.2k
Fork
372
Merge trung bình
1 ngày 14 giờ
Pull request đã merge (30 ngày)
241

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của TanStack/ai

Tất cả issue của TanStack/ai

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.