{"record":{"id":"40265da8a2d59905","repo":"wavetermdev/waveterm","slug":"convertfileaimessagepart-expects-file-type-got","errorCode":null,"errorMessage":"convertFileAIMessagePart expects 'file' type, got '%s'","messagePattern":"convertFileAIMessagePart expects 'file' type, got '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/anthropic/anthropic-convertmessage.go","lineNumber":539,"sourceCode":"\t\tdata := parts[1]\n\n\t\t// Check if it's already base64 encoded: data:mediatype;base64,<data>\n\t\tif strings.Contains(header, \";base64\") {\n\t\t\t// Already base64 encoded\n\t\t\treturn data, nil\n\t\t} else {\n\t\t\t// Raw data that needs base64 encoding: data:mediatype,<raw_data>\n\t\t\treturn base64.StdEncoding.EncodeToString([]byte(data)), nil\n\t\t}\n\t}\n\n\treturn \"\", errors.New(\"no data available for base64 extraction\")\n}\n\n// convertFileAIMessagePart converts a file AIMessagePart to anthropicMessageContentBlock\nfunc convertFileAIMessagePart(part uctypes.AIMessagePart) (*anthropicMessageContentBlock, error) {\n\tif part.Type != uctypes.AIMessagePartTypeFile {\n\t\treturn nil, fmt.Errorf(\"convertFileAIMessagePart expects 'file' type, got '%s'\", part.Type)\n\t}\n\n\tif err := part.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Validate URL protocol if URL is provided - only allow data:, http:, https:\n\tif part.URL != \"\" {\n\t\tif !strings.HasPrefix(part.URL, \"data:\") &&\n\t\t\t!strings.HasPrefix(part.URL, \"http://\") &&\n\t\t\t!strings.HasPrefix(part.URL, \"https://\") {\n\t\t\treturn nil, fmt.Errorf(\"unsupported URL protocol in file part: %s\", part.URL)\n\t\t}\n\t}\n\n\t// Branch on mimetype to determine block type and constraints\n\tswitch {\n\tcase strings.HasPrefix(part.MimeType, \"image/\"):","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/anthropic/anthropic-convertmessage.go#L521-L557","documentation":"convertFileAIMessagePart is an internal helper that only handles AIMessageParts whose Type is AIMessagePartTypeFile. It rejects anything else up front, so this error indicates the public converter dispatched a non-file part into the file branch — an internal dispatch inconsistency or a caller constructing parts with a wrong Type value.","triggerScenarios":"An AIMessagePart reaching convertFileAIMessagePart with Type != AIMessagePartTypeFile (e.g. \"\", \"text\", or an unknown type string), via ConvertAIMessageToAnthropicChatMessage's file case.","commonSituations":"Custom part types added downstream without updating the converter; type constants defined with differing string values between packages; reflection/manual part construction that skips Type.","solutions":["Ensure part.Type == uctypes.AIMessagePartTypeFile before the part enters the file path","Use the exported constant rather than a raw string literal for Type","Fix the dispatch in ConvertAIMessageToAnthropicChatMessage callers if non-file parts are being routed here"],"exampleFix":"// before\npart := uctypes.AIMessagePart{Type: \"file\", MimeType: \"image/png\", Data: imgBytes} // literal differs from constant\n// after\npart := uctypes.AIMessagePart{Type: uctypes.AIMessagePartTypeFile, MimeType: \"image/png\", Data: imgBytes}","handlingStrategy":"type-guard","validationCode":"func isFileAIMessagePart(p uctypes.AIMessagePart) bool { return p.Type == uctypes.AIMessagePartTypeFile }\nif !isFileAIMessagePart(part) { return fmt.Errorf(\"not a file part: %q\", part.Type) }","typeGuard":"func isFileAIMessagePart(p uctypes.AIMessagePart) bool {\n    return p.Type == uctypes.AIMessagePartTypeFile\n}","tryCatchPattern":"if err := convert(aiMsg); err != nil {\n    if strings.Contains(err.Error(), \"convertFileAIMessagePart expects 'file'\") {\n        log.Printf(\"non-file part reached file converter: %+v\", part)\n        return nil // skip or reroute\n    }\n    return err\n}","preventionTips":["Set Type via uctypes.AIMessagePartTypeFile, never raw literals","Confirm dispatch logic routes only file parts into file converters","Add tests asserting each part type takes its intended conversion branch"],"tags":["go","type-mismatch","anthropic","message-conversion"],"backgroundTag":"wrong-part-type","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}