{"record":{"id":"5fc684642c1ffd4f","repo":"wavetermdev/waveterm","slug":"text-type-cannot-have-file-fields-set","errorCode":null,"errorMessage":"text type cannot have file fields set","messagePattern":"text type cannot have file fields set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/uctypes/uctypes.go","lineNumber":404,"sourceCode":"\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\n\tif p.Type == AIMessagePartTypeFile {\n\t\tif p.Text != \"\" {\n\t\t\treturn fmt.Errorf(\"file type cannot have text field set\")\n\t\t}\n\n\t\tif p.MimeType == \"\" {\n\t\t\treturn fmt.Errorf(\"file type requires mimetype\")\n\t\t}\n\n\t\t// Either data or url (not both) must be set\n\t\thasData := len(p.Data) > 0\n\t\thasURL := p.URL != \"\"\n\n\t\tif !hasData && !hasURL {","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/uctypes/uctypes.go#L386-L422","documentation":"AIMessagePart.Validate() rejects a \"text\" part that also sets file-only fields (FileName, MimeType, Data, URL). Part types are mutually exclusive: a text part is pure text and must not smuggle attachment fields.","triggerScenarios":"Constructing AIMessagePart{Type:\"text\", Text:\"hi\", MimeType:\"image/png\"} (or FileName/Data/URL set) and calling Validate(). Also happens when reusing/re-purposing a file part struct and only changing Type to \"text\" without clearing the file fields.","commonSituations":"Copied struct literals reused for a different part type; unmarshaling loose JSON that includes both text and file keys; refactoring code that merged text and attachment handling.","solutions":["Remove the file fields (FileName, MimeType, Data, URL) from the text part","Split into two parts: a text part and a separate file part","Reset the part struct (zero value) before setting the text fields"],"exampleFix":"// before\npart := uctypes.AIMessagePart{Type: \"text\", Text: \"see attachment\", FileName: \"a.png\", MimeType: \"image/png\"}\n// after\nparts := []uctypes.AIMessagePart{\n\t{Type: \"text\", Text: \"see attachment\"},\n\t{Type: \"file\", FileName: \"a.png\", MimeType: \"image/png\", Data: data},\n}","handlingStrategy":"validation","validationCode":"func validPureTextPart(p uctypes.AIMessagePart) bool {\n\treturn p.Type == uctypes.AIMessagePartTypeText && p.Text != \"\" &&\n\t\tp.FileName == \"\" && p.MimeType == \"\" && len(p.Data) == 0 && p.URL == \"\"\n}","typeGuard":"func asPureTextPart(p uctypes.AIMessagePart) (string, bool) {\n\tif p.Type == uctypes.AIMessagePartTypeText && p.Text != \"\" &&\n\t\tp.FileName == \"\" && p.MimeType == \"\" && len(p.Data) == 0 && p.URL == \"\" {\n\t\treturn p.Text, true\n\t}\n\treturn \"\", false\n}","tryCatchPattern":null,"preventionTips":["Start from the zero-value AIMessagePart and set only the fields for the chosen type","Split captions and attachments into separate parts","Use constants AIMessagePartTypeText/AIMessagePartTypeFile instead of raw strings"],"tags":["validation","ai-messages"],"backgroundTag":"schema-validation-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}