Tencent/WeKnora · error
no file key (URL or media_id) in message
Error message
no file key (URL or media_id) in message
What it means
DownloadFile extracts the file reference from msg.FileKey (a PicUrl or MediaId). When the incoming WeCom message carries neither — FileKey is empty — there is nothing to download and the method returns this error. WeCom text and event messages legitimately contain no file, so callers must check before invoking.
Source
Thrown at internal/im/wecom/webhook_adapter.go:539
PicUrl string `xml:"PicUrl"` // image: download URL
MediaId string `xml:"MediaId"` // image/voice/video: media ID for download
Format string `xml:"Format"` // voice: audio format (amr/speex)
ThumbMediaId string `xml:"ThumbMediaId"` // video: thumbnail media ID
MsgID string `xml:"MsgId"`
AgentID string `xml:"AgentID"`
ChatID string `xml:"ChatId"`
}
// ──────────────────────────────────────────────────────────────────────
// File download support for WeCom webhook mode
// ──────────────────────────────────────────────────────────────────────
// DownloadFile downloads a file/image from WeCom.
// For webhook mode, images come with MediaId (temporary media) which can be
// downloaded via the GetMedia API, or PicUrl for direct download.
func (a *WebhookAdapter) DownloadFile(ctx context.Context, msg *im.IncomingMessage) (io.ReadCloser, string, error) {
if msg.FileKey == "" {
return nil, "", fmt.Errorf("no file key (URL or media_id) in message")
}
fileName := msg.FileName
if fileName == "" {
fileName = msg.FileKey
}
// If FileKey looks like a URL, download directly
if strings.HasPrefix(msg.FileKey, "http://") || strings.HasPrefix(msg.FileKey, "https://") {
return downloadFromURL(ctx, msg.FileKey, fileName, a.extraAllowedHost)
}
// Otherwise treat as media_id, download via temporary media API
accessToken, err := a.getAccessToken(ctx)
if err != nil {
return nil, "", fmt.Errorf("get access token: %w", err)
}
View on GitHub (pinned to 988cbb0330)
Solutions
- Check msg.FileKey != "" (or msg type) before calling DownloadFile
- Verify the adapter's ParseCallback/parse logic maps PicUrl and MediaId to FileKey for the message types you receive
- Log msg type for messages hitting this path to confirm only genuinely file-less types reach it
- Skip or handle differently messages of type text/event instead of attempting a download
Example fix
// before
rc, name, err := adapter.DownloadFile(ctx, msg)
// after
if msg.FileKey == "" { return nil } // not a file message
rc, name, err := adapter.DownloadFile(ctx, msg) Defensive patterns
Strategy: validation
Validate before calling
if msg.FileKey == "" { /* skip download — message has no attachment */ } Type guard
func hasDownloadableFile(msg *im.IncomingMessage) bool { return msg.FileKey != "" } Try / catch
if msg.FileKey == "" { return nil } // guard before call
rc, name, err := adapter.DownloadFile(ctx, msg)
if err != nil && strings.Contains(err.Error(), "no file key") {
logger.Debugf("message %s has no file", msg.ID)
return nil
} Prevention
- Check message type before attempting downloads
- Ensure adapter parsing maps PicUrl/MediaId into FileKey for all supported message types
- Cover each incoming message type with a parse test asserting FileKey presence
When it happens
Trigger: Calling DownloadFile on an IncomingMessage parsed from a text/event message (no PicUrl, no MediaId); a message type where the adapter failed to populate FileKey; forwarding messages that lost their media reference.
Common situations: Treating every incoming callback as having an attachment; upstream normalization not mapping PicUrl/MediaId into FileKey for a new message type; users sending stickers or non-image types the adapter doesn't map.
Related errors
- URL rejected for security reasons: %v
- download failed: status=%d
- no file URL in message
- wiki page slug is required
- knowledge_base_id is required
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/cf79404b9ff19a8f.
Report an issue: GitHub.