{"record":{"id":"ac09ff79b60ec020","repo":"wavetermdev/waveterm","slug":"part-d-w-ac09ff","errorCode":null,"errorMessage":"part %d: %w","messagePattern":"part (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/uctypes/uctypes.go","lineNumber":390,"sourceCode":"\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 == \"\" {\n\t\t\treturn fmt.Errorf(\"text type requires non-empty text field\")\n\t\t}\n\t\t// Check that no file fields are set\n\t\tif p.FileName != \"\" || p.MimeType != \"\" || len(p.Data) > 0 || p.URL != \"\" {\n\t\t\treturn fmt.Errorf(\"text type cannot have file fields set\")\n\t\t}\n\t\treturn nil\n\t}\n","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/uctypes/uctypes.go#L372-L408","documentation":"After checking MessageId and non-empty Parts, AIMessage.Validate delegates to each part's own Validate() and wraps any failure as \"part %d: %w\" with the part's index. This tells you exactly which element in the Parts slice is invalid while preserving the underlying reason (invalid part type, empty content, etc.).","triggerScenarios":"Validate() on an AIMessage whose i-th part fails its own validation — e.g. a tool-use part missing required fields, an empty text part, or a nil/unsupported part at that index.","commonSituations":"Corrupted or partially-deserialized messages from external sources; hand-built parts missing required sub-fields; protocol changes where older part shapes no longer validate.","solutions":["Read the wrapped inner error to see which part field failed, and fix that part at the reported index","Validate each part individually (part.Validate()) to isolate the failure before assembling the message","Regenerate or re-fetch the message if it came from an external/corrupted source","Update part construction helpers so required sub-fields are always populated"],"exampleFix":"// before\nparts := []uctypes.MessagePart{toolUsePart /* missing tool name */}\nmsg.Validate() // \"part 0: ...\"\n// after\nfor i, p := range parts { if err := p.Validate(); err != nil { log.Printf(\"fix part %d: %v\", i, err) } }\ntoolUsePart.ToolName = \"delete_text_file\"\nmsg.Validate() // nil","handlingStrategy":"validation","validationCode":"for i, part := range msg.Parts {\n    if err := part.Validate(); err != nil {\n        return fmt.Errorf(\"part %d invalid before send: %w\", i, err)\n    }\n}","typeGuard":"func allPartsValid(m *uctypes.AIMessage) bool {\n    if m == nil { return false }\n    for _, p := range m.Parts {\n        if p.Validate() != nil { return false }\n    }\n    return true\n}","tryCatchPattern":"if err := msg.Validate(); err != nil {\n    var idx int\n    if n, _ := fmt.Sscanf(err.Error(), \"part %d:\", &idx); n == 1 {\n        log.Printf(\"invalid part at index %d: %v\", idx, err)\n    }\n    return err\n}","preventionTips":["Validate each part with part.Validate() as it is created","Use constructors/helpers that populate all required part fields","Reject malformed parts at deserialization time with index context","Track protocol version changes that alter part validation rules"],"tags":["validation","message-protocol","nested-error"],"backgroundTag":"schema-validation-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}