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

  1. Check msg.FileKey != "" (or msg type) before calling DownloadFile
  2. Verify the adapter's ParseCallback/parse logic maps PicUrl and MediaId to FileKey for the message types you receive
  3. Log msg type for messages hitting this path to confirm only genuinely file-less types reach it
  4. 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

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


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