Tencent/WeKnora · error

attachment has no file extension

Error message

attachment has no file extension

What it means

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.

Solutions

  1. Rename the file to include an extension before sending
  2. Ensure your adapter's DownloadFile returns a fileName with an extension (derive from Content-Type/MIME if needed)
  3. Set msg.FileName to a name with an extension in your adapter/webhook layer

Example fix

// before
return reader, "download", nil // no extension
// after
return reader, "download.pdf", nil // extension derived from Content-Type
Defensive patterns

Strategy: validation

Validate before calling

if msg.MessageType == im.MessageTypeFile && filepath.Ext(msg.FileName) == "" {
    return errors.New("file must have an extension")
}

Type guard

func hasExtension(name string) bool { return filepath.Ext(name) != "" }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/69fb53b1bf2aa03d. Report an issue: GitHub.

Appendix: source

Thrown at internal/im/service.go:638

		return nil, nil, nil, err
	}
	defer reader.Close()
	content, err := io.ReadAll(io.LimitReader(reader, maxIMAttachmentBytes+1))
	if err != nil {
		return nil, nil, nil, err
	}
	if len(content) > maxIMAttachmentBytes {
		return nil, nil, nil, fmt.Errorf("attachment exceeds the %d MiB limit", maxIMAttachmentBytes>>20)
	}
	if fileName == "" {
		fileName = msg.FileName
	}
	if msg.MessageType == MessageTypeImage && filepath.Ext(fileName) == "" {
		fileName += ".png"
	}
	ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(fileName), "."))
	if ext == "" {
		return nil, nil, nil, fmt.Errorf("attachment has no file extension")
	}
	attachment := types.MessageAttachment{FileName: fileName, FileType: "." + ext, FileSize: int64(len(content))}
	request := &types.ReadRequest{FileContent: content, FileName: fileName, FileType: ext}
	var result *types.ReadResult
	isImage := msg.MessageType == MessageTypeImage || docparser.IsImageFormat(ext)
	if isImage && s.documentReader != nil {
		result, err = s.documentReader.Read(attachmentCtx, request)
		if err != nil {
			logger.Warnf(ctx, "[IM] image OCR/document parsing failed, continuing with vision input: %v", err)
			result, err = nil, nil
		}
	}
	if result == nil && docparser.IsSimpleFormat(attachment.FileType) {
		result, err = (&docparser.SimpleFormatReader{}).Read(attachmentCtx, request)
	} else if result == nil && !isImage && s.documentReader != nil {
		result, err = s.documentReader.Read(attachmentCtx, request)
	}
	if err != nil {

View on GitHub (pinned to 988cbb0330)