{"record":{"id":"ec2bb33437d55580","repo":"wavetermdev/waveterm","slug":"messageid-must-be-set","errorCode":null,"errorMessage":"messageid must be set","messagePattern":"messageid must be set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/uctypes/uctypes.go","lineNumber":381,"sourceCode":"\tURL        string `json:\"url,omitempty\"`\n\tSize       int    `json:\"size,omitempty\"`\n\tPreviewUrl string `json:\"previewurl,omitempty\"` // 128x128 webp data url for images\n}\n\ntype AIToolResult struct {\n\tToolName  string `json:\"toolname\"`\n\tToolUseID string `json:\"tooluseid\"`\n\tErrorText string `json:\"errortext,omitempty\"`\n\tText      string `json:\"text,omitempty\"`\n}\n\nfunc (m *AIMessage) GetMessageId() string {\n\treturn m.MessageId\n}\n\nfunc (m *AIMessage) Validate() error {\n\tif m.MessageId == \"\" {\n\t\treturn fmt.Errorf(\"messageid must be set\")\n\t}\n\n\tif len(m.Parts) == 0 {\n\t\treturn fmt.Errorf(\"parts must not be empty\")\n\t}\n\n\tfor i, part := range m.Parts {\n\t\tif err := part.Validate(); err != nil {\n\t\t\treturn fmt.Errorf(\"part %d: %w\", i, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (p *AIMessagePart) Validate() error {\n\tif p.Type == AIMessagePartTypeText {\n\t\tif p.Text == \"\" {","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/uctypes/uctypes.go#L363-L399","documentation":"AIMessage.Validate enforces that every AI message carries a non-empty MessageId, which is used for correlation, streaming, and deduplication in the UI message protocol. A message constructed or deserialized without an ID fails validation before it is accepted into the conversation pipeline.","triggerScenarios":"Calling Validate() on an AIMessage whose MessageId field is \"\" — e.g. after unmarshaling JSON lacking the messageid, or constructing AIMessage{} manually.","commonSituations":"Deserializing messages from an external source that omits the id; hand-constructed test messages; migrating from an older wire format without message ids.","solutions":["Set MessageId before calling Validate, using a unique id (uuid or existing conversation-scoped id)","Check the JSON/serialization source actually includes the message id field","In test code, use a helper/factory that always assigns MessageId","Add validation at deserialization boundaries so bad data is rejected early"],"exampleFix":"// before\nmsg := &uctypes.AIMessage{Parts: parts}\nerr := msg.Validate() // \"messageid must be set\"\n// after\nmsg := &uctypes.AIMessage{MessageId: uuid.NewString(), Parts: parts}\nerr := msg.Validate()","handlingStrategy":"validation","validationCode":"if msg == nil || msg.MessageId == \"\" {\n    return fmt.Errorf(\"AIMessage requires a non-empty MessageId\")\n}","typeGuard":"func isValidAIMessage(m *uctypes.AIMessage) bool {\n    return m != nil && m.MessageId != \"\"\n}","tryCatchPattern":"if err := msg.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"messageid must be set\") {\n        msg.MessageId = uuid.NewString()\n        err = msg.Validate()\n    }\n}","preventionTips":["Assign MessageId at message construction time, never afterward","Validate messages right after deserialization to catch missing ids early","Use a message factory that guarantees id assignment","Check wire-format sources actually emit the message id field"],"tags":["validation","message-protocol","missing-field"],"backgroundTag":"missing-required-argument","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}