Tencent/WeKnora · error
no file URL in message
Error message
no file URL in message
What it means
DownloadFile downloads and decrypts the media attached to a WeCom aibot message. Each incoming message carries an encrypted URL in FileKey; when that field is empty there is nothing to download, so the adapter returns this error. It guards against messages that have no file payload (e.g. plain text) or were parsed incompletely.
Source
Thrown at internal/im/wecom/ws_adapter.go:85
func (a *WSAdapter) FinalizeStream(ctx context.Context, incoming *im.IncomingMessage, streamID string, finalContent string) error {
return a.client.FinalizeStream(ctx, incoming, streamID, finalContent)
}
func (a *WSAdapter) SendStreamChunk(ctx context.Context, incoming *im.IncomingMessage, streamID string, content string) error {
return a.client.UpdateStreamContent(ctx, incoming, streamID, content)
}
func (a *WSAdapter) EndStream(ctx context.Context, incoming *im.IncomingMessage, streamID string) error {
return a.client.EndStream(ctx, incoming, streamID)
}
// ── FileDownloader implementation ──
// WeCom aibot provides AES-256-CBC encrypted URLs for image/file/video messages.
// Each message carries its own aeskey for decryption.
func (a *WSAdapter) DownloadFile(ctx context.Context, msg *im.IncomingMessage) (io.ReadCloser, string, error) {
if msg.FileKey == "" {
return nil, "", fmt.Errorf("no file URL in message")
}
fileName := msg.FileName
if fileName == "" {
fileName = msg.FileKey
}
// Download the (encrypted) file content
reader, fileName, err := downloadFromURL(ctx, msg.FileKey, fileName, a.client.extraAllowedHost)
if err != nil {
return nil, "", err
}
// If an AES key is provided, the downloaded content is AES-256-CBC encrypted
// and must be decrypted before use. This is the case for WeCom aibot long
// connection mode where each file/image message carries a per-message aeskey.
aesKeyB64 := msg.Extra["aes_key"]
if aesKeyB64 == "" {
// No encryption — return raw content (e.g. webhook mode uses media API)View on GitHub (pinned to 988cbb0330)
Solutions
- Check msg.FileKey (or the message type) is non-empty before calling DownloadFile.
- Skip download for non-media message types (text, event, etc.).
- Log the raw message payload to confirm the parser populated FileKey; if media messages consistently lack it, upgrade/fix the callback parsing code.
Example fix
// before
rc, name, err := adapter.DownloadFile(ctx, msg)
// after
if msg.FileKey == "" {
return nil, nil // not a media message
}
rc, name, err := adapter.DownloadFile(ctx, msg) Defensive patterns
Strategy: validation
Validate before calling
if msg.FileKey == "" {
// not a media message; skip download
return nil
}
rc, name, err := adapter.DownloadFile(ctx, msg) Type guard
func hasDownloadableFile(msg *im.IncomingMessage) bool {
return msg != nil && msg.FileKey != ""
} Try / catch
rc, name, err := adapter.DownloadFile(ctx, msg)
if err != nil && strings.Contains(err.Error(), "no file URL in message") {
return nil, nil // expected for text messages
} Prevention
- Check message type before attempting file download.
- Log raw incoming payloads when FileKey is unexpectedly empty on media messages.
- Keep the callback/message parser up to date so FileKey is always populated for media.
When it happens
Trigger: Calling DownloadFile with an *im.IncomingMessage whose FileKey is empty — typically a text message, or a media message whose parse step failed to populate FileKey.
Common situations: Downloading attachments for every incoming message without checking message type first; upstream parser version mismatch leaving FileKey unset; voice/emoji messages that carry no downloadable URL.
Related errors
- wiki page slug is required
- knowledge_base_id is required
- ciphertext too short
- corp_id mismatch: expected %s, got %s
- no file key (URL or media_id) in message
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/47e92a03358b6982.
Report an issue: GitHub.