{"record":{"id":"69fb53b1bf2aa03d","repo":"Tencent/WeKnora","slug":"attachment-has-no-file-extension","errorCode":null,"errorMessage":"attachment has no file extension","messagePattern":"attachment has no file extension","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/service.go","lineNumber":638,"sourceCode":"\t\treturn nil, nil, nil, err\n\t}\n\tdefer reader.Close()\n\tcontent, err := io.ReadAll(io.LimitReader(reader, maxIMAttachmentBytes+1))\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tif len(content) > maxIMAttachmentBytes {\n\t\treturn nil, nil, nil, fmt.Errorf(\"attachment exceeds the %d MiB limit\", maxIMAttachmentBytes>>20)\n\t}\n\tif fileName == \"\" {\n\t\tfileName = msg.FileName\n\t}\n\tif msg.MessageType == MessageTypeImage && filepath.Ext(fileName) == \"\" {\n\t\tfileName += \".png\"\n\t}\n\text := strings.ToLower(strings.TrimPrefix(filepath.Ext(fileName), \".\"))\n\tif ext == \"\" {\n\t\treturn nil, nil, nil, fmt.Errorf(\"attachment has no file extension\")\n\t}\n\tattachment := types.MessageAttachment{FileName: fileName, FileType: \".\" + ext, FileSize: int64(len(content))}\n\trequest := &types.ReadRequest{FileContent: content, FileName: fileName, FileType: ext}\n\tvar result *types.ReadResult\n\tisImage := msg.MessageType == MessageTypeImage || docparser.IsImageFormat(ext)\n\tif isImage && s.documentReader != nil {\n\t\tresult, err = s.documentReader.Read(attachmentCtx, request)\n\t\tif err != nil {\n\t\t\tlogger.Warnf(ctx, \"[IM] image OCR/document parsing failed, continuing with vision input: %v\", err)\n\t\t\tresult, err = nil, nil\n\t\t}\n\t}\n\tif result == nil && docparser.IsSimpleFormat(attachment.FileType) {\n\t\tresult, err = (&docparser.SimpleFormatReader{}).Read(attachmentCtx, request)\n\t} else if result == nil && !isImage && s.documentReader != nil {\n\t\tresult, err = s.documentReader.Read(attachmentCtx, request)\n\t}\n\tif err != nil {","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/service.go#L620-L656","documentation":"Every IM attachment must carry a file extension, which is used to set attachment FileType and select the document/image parser. If the resolved file name (from the downloader or msg.FileName) has no extension, processing aborts with this error.","triggerScenarios":"A file or image message whose fileName (from DownloadFile or msg.FileName) has an empty extension after normalization. Images get a fallback \".png\" only when msg.MessageType is image; non-image files with no extension fail.","commonSituations":"Users uploading extension-less files (common on macOS/Linux); platforms sending sanitized or stripped file names; downloads where the server supplies a generic name with no extension.","solutions":["Rename the file to include an extension before sending","Ensure your adapter's DownloadFile returns a fileName with an extension (derive from Content-Type/MIME if needed)","Set msg.FileName to a name with an extension in your adapter/webhook layer"],"exampleFix":"// before\nreturn reader, \"download\", nil // no extension\n// after\nreturn reader, \"download.pdf\", nil // extension derived from Content-Type","handlingStrategy":"validation","validationCode":"if msg.MessageType == im.MessageTypeFile && filepath.Ext(msg.FileName) == \"\" {\n    return errors.New(\"file must have an extension\")\n}","typeGuard":"func hasExtension(name string) bool { return filepath.Ext(name) != \"\" }","tryCatchPattern":null,"preventionTips":["Instruct users to send files with proper extensions","In custom adapters, derive a fileName extension from Content-Type/MIME when the platform omits it"],"tags":["attachments","file-extension","validation"],"backgroundTag":"missing-file-extension","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}