datawhalechina/hello-agents · error · ValueError

消息 {at_message_id} 不存在

Error message

消息 {at_message_id} 不存在

What it means

Conversation.fork raises ValueError when at_message_id does not match any message_id in the conversation's message list. Forking needs an anchor message to copy history up to (inclusive) and mark as the branch point, so an unknown anchor is unrecoverable.

Source

Thrown at Co-creation-projects/lcyting-StockSage-agent/HelloAgents Optimized/hello_agents/core/conversation.py:56

        return self.messages[start:end]

    def get_last_message(self) -> Optional[Message]:
        return self.messages[-1] if self.messages else None

    def get_message_by_id(self, message_id: str) -> Optional[Message]:
        for m in self.messages:
            if m.message_id == message_id:
                return m
        return None

    def fork(self, at_message_id: str, new_name: str = "") -> "Conversation":
        target_idx = -1
        for i, m in enumerate(self.messages):
            if m.message_id == at_message_id:
                target_idx = i
                break
        if target_idx == -1:
            raise ValueError(f"消息 {at_message_id} 不存在")

        new_conv = Conversation(
            name=new_name or f"{self.name} (分支)",
            system_prompt=self.system_prompt,
            metadata={**self.metadata, "forked_from": self.conversation_id},
        )

        for i, m in enumerate(self.messages[: target_idx + 1]):
            if i == target_idx:
                fork_msg = m.model_copy(deep=True)
                fork_msg.branch_point = True
                fork_msg.conversation_id = new_conv.conversation_id
                fork_msg.parent_id = (
                    new_conv.messages[-1].message_id if new_conv.messages else None
                )
                new_conv.messages.append(fork_msg)
            else:
                copied = m.model_copy(deep=True)

View on GitHub (pinned to 606a07d341)

Solutions

  1. Use a message_id taken directly from conversation.messages of the same conversation object.
  2. Refresh the conversation state in the UI before offering a fork action.
  3. If the anchor was deleted, fork from the current last message (messages[-1].message_id) instead.

Example fix

# before
new_conv = conv.fork(at_message_id=user_picked_id)  # ValueError if stale

# after
ids = [m.message_id for m in conv.messages]
if user_picked_id not in ids:
    user_picked_id = ids[-1]
new_conv = conv.fork(at_message_id=user_picked_id)
Defensive patterns

Strategy: type-guard

Validate before calling

message_ids = {m.message_id for m in conv.messages}
if at_message_id not in message_ids:
    at_message_id = conv.messages[-1].message_id  # fork from latest
new_conv = conv.fork(at_message_id=at_message_id)

Type guard

def is_fork_anchor(conv: "Conversation", message_id: str) -> bool:
    return any(m.message_id == message_id for m in conv.messages)

Try / catch

try:
    new_conv = conv.fork(at_message_id=anchor_id)
except ValueError as e:
    if "不存在" in str(e):
        anchor_id = conv.messages[-1].message_id
        new_conv = conv.fork(at_message_id=anchor_id)
    else:
        raise

Prevention

When it happens

Trigger: Calling fork(at_message_id=...) with an id from another conversation, a deleted message, or a malformed/truncated id string.

Common situations: UI passing a message id from a stale conversation snapshot; concurrent deletion of messages between render and fork; ids persisted before a schema change.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/f7ed9d325c7f87dc. Report an issue: GitHub.