sipeed/picoclaw · error
wecom upload finish returned empty media_id
Error message
wecom upload finish returned empty media_id
What it means
The WeCom chunked upload finished (all chunks sent, upload/finish acknowledged, response decoded) but the ack contained an empty media_id. Since media_id is required to reference the uploaded asset in the outbound message, the channel discards the result and errors out. Like error 662 this is a server/protocol anomaly, not bad input from the caller.
Source
Thrown at pkg/channels/wecom/media.go:752
}
}
finishEnv, err := c.sendCommandAck(wecomCommand{
Cmd: wecomCmdUploadMediaEnd,
Headers: wecomHeaders{ReqID: randomID(10)},
Body: wecomUploadMediaFinishBody{
UploadID: initResp.UploadID,
},
}, wecomUploadTimeout)
if err != nil {
return nil, err
}
finishResp, err := decodeWeComEnvelopeBody[wecomUploadMediaFinishResponse](finishEnv)
if err != nil {
return nil, err
}
if strings.TrimSpace(finishResp.MediaID) == "" {
return nil, fmt.Errorf("wecom upload finish returned empty media_id")
}
uploaded := &wecomOutboundMedia{
MsgType: kind,
MediaID: finishResp.MediaID,
}
if kind == "video" {
video := buildWeComVideoContent(finishResp.MediaID, filename, part.Caption)
uploaded.Title = video.Title
uploaded.Description = video.Description
}
return uploaded, nil
}
func fallbackWeComMediaText(part bus.MediaPart, kind, filename string) string {
var lines []string
if caption := strings.TrimSpace(part.Caption); caption != "" {
lines = append(lines, caption)View on GitHub (pinned to 49183d7e8d)
Solutions
- Retry the media send once — the whole init/chunk/finish cycle restarts with a fresh upload_id
- Keep individual attachments well under the 20MB cap so the finish command is not delayed by long chunk transfers
- If it persists, dump the finish ack body and verify the wecomUploadMediaFinishResponse field mapping against the current WeCom protocol
- Rely on the built-in placeholder fallback for user-facing delivery while investigating
Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
func isWecomEmptyMediaID(err error) bool {
return err != nil && strings.Contains(err.Error(), "empty media_id")
} Try / catch
if err := ch.Send(msg); err != nil {
if isWecomEmptyMediaID(err) {
// full init/chunk/finish restarts on retry; usually succeeds
time.Sleep(3 * time.Second)
err = ch.Send(msg)
}
} Prevention
- Retry once after a short pause — server-side finish glitches are transient
- Avoid max-size uploads that stretch the chunk phase across server maintenance windows
- Rely on the channel's placeholder fallback for user-visible delivery while retrying
When it happens
Trigger: A full uploadOutboundMedia cycle where wecomCmdUploadMediaFinish returns a well-formed envelope whose MediaID field is empty or whitespace — e.g. the server expired the upload session between the last chunk and finish, or the finish ack schema changed.
Common situations: Long-running uploads that straddle a gateway restart or session expiry; transient server-side storage failure; WeCom protocol update renaming the field; note the caller-side code at media.go:752 already falls back to a placeholder message, so users typically see the fallback rather than a hard failure unless the error propagates.
Related errors
- wecom upload init returned empty upload_id
- wecom response body is empty
- unsupported wecom media type or size for %q
- wecom upload requires 1-%d chunks, got %d
- discord send media: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/ef489b03b8349f24.
Report an issue: GitHub.