{"record":{"id":"f7ed9d325c7f87dc","repo":"datawhalechina/hello-agents","slug":"at-message-id","errorCode":null,"errorMessage":"消息 {at_message_id} 不存在","messagePattern":"消息 (.+?) 不存在","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/lcyting-StockSage-agent/HelloAgents Optimized/hello_agents/core/conversation.py","lineNumber":56,"sourceCode":"        return self.messages[start:end]\n\n    def get_last_message(self) -> Optional[Message]:\n        return self.messages[-1] if self.messages else None\n\n    def get_message_by_id(self, message_id: str) -> Optional[Message]:\n        for m in self.messages:\n            if m.message_id == message_id:\n                return m\n        return None\n\n    def fork(self, at_message_id: str, new_name: str = \"\") -> \"Conversation\":\n        target_idx = -1\n        for i, m in enumerate(self.messages):\n            if m.message_id == at_message_id:\n                target_idx = i\n                break\n        if target_idx == -1:\n            raise ValueError(f\"消息 {at_message_id} 不存在\")\n\n        new_conv = Conversation(\n            name=new_name or f\"{self.name} (分支)\",\n            system_prompt=self.system_prompt,\n            metadata={**self.metadata, \"forked_from\": self.conversation_id},\n        )\n\n        for i, m in enumerate(self.messages[: target_idx + 1]):\n            if i == target_idx:\n                fork_msg = m.model_copy(deep=True)\n                fork_msg.branch_point = True\n                fork_msg.conversation_id = new_conv.conversation_id\n                fork_msg.parent_id = (\n                    new_conv.messages[-1].message_id if new_conv.messages else None\n                )\n                new_conv.messages.append(fork_msg)\n            else:\n                copied = m.model_copy(deep=True)","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/lcyting-StockSage-agent/HelloAgents Optimized/hello_agents/core/conversation.py#L38-L74","documentation":"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.","triggerScenarios":"Calling fork(at_message_id=...) with an id from another conversation, a deleted message, or a malformed/truncated id string.","commonSituations":"UI passing a message id from a stale conversation snapshot; concurrent deletion of messages between render and fork; ids persisted before a schema change.","solutions":["Use a message_id taken directly from conversation.messages of the same conversation object.","Refresh the conversation state in the UI before offering a fork action.","If the anchor was deleted, fork from the current last message (messages[-1].message_id) instead."],"exampleFix":"# before\nnew_conv = conv.fork(at_message_id=user_picked_id)  # ValueError if stale\n\n# after\nids = [m.message_id for m in conv.messages]\nif user_picked_id not in ids:\n    user_picked_id = ids[-1]\nnew_conv = conv.fork(at_message_id=user_picked_id)","handlingStrategy":"type-guard","validationCode":"message_ids = {m.message_id for m in conv.messages}\nif at_message_id not in message_ids:\n    at_message_id = conv.messages[-1].message_id  # fork from latest\nnew_conv = conv.fork(at_message_id=at_message_id)","typeGuard":"def is_fork_anchor(conv: \"Conversation\", message_id: str) -> bool:\n    return any(m.message_id == message_id for m in conv.messages)","tryCatchPattern":"try:\n    new_conv = conv.fork(at_message_id=anchor_id)\nexcept ValueError as e:\n    if \"不存在\" in str(e):\n        anchor_id = conv.messages[-1].message_id\n        new_conv = conv.fork(at_message_id=anchor_id)\n    else:\n        raise","preventionTips":["Always source the anchor id from the same conversation object being forked.","Refresh conversation state in the UI before exposing fork actions.","Fall back to forking at the latest message when the anchor is gone."],"tags":["validation","conversation-tree","pydantic","python"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}