sipeed/picoclaw · error
wecom upload init returned empty upload_id
Error message
wecom upload init returned empty upload_id
What it means
The WeCom upload protocol completed the upload/init handshake without error (the ack envelope decoded), but the response carried an empty or whitespace-only upload_id. Every subsequent chunk/finish command needs that upload_id, so the channel aborts immediately. This indicates a server-side or protocol-level anomaly rather than a client mistake.
Source
Thrown at pkg/channels/wecom/media.go:715
Cmd: wecomCmdUploadMediaInit,
Headers: wecomHeaders{ReqID: randomID(10)},
Body: wecomUploadMediaInitBody{
Type: kind,
Filename: filename,
TotalSize: size,
TotalChunks: totalChunks,
MD5: hex.EncodeToString(sum[:]),
},
}, wecomUploadTimeout)
if err != nil {
return nil, err
}
initResp, err := decodeWeComEnvelopeBody[wecomUploadMediaInitResponse](initEnv)
if err != nil {
return nil, err
}
if strings.TrimSpace(initResp.UploadID) == "" {
return nil, fmt.Errorf("wecom upload init returned empty upload_id")
}
for idx, offset := 0, 0; offset < len(data); idx, offset = idx+1, offset+wecomUploadChunkMaxBytes {
end := offset + wecomUploadChunkMaxBytes
if end > len(data) {
end = len(data)
}
sendErr := c.sendCommand(wecomCommand{
Cmd: wecomCmdUploadMediaChunk,
Headers: wecomHeaders{ReqID: randomID(10)},
Body: wecomUploadMediaChunkBody{
UploadID: initResp.UploadID,
ChunkIndex: idx,
Base64Data: base64.StdEncoding.EncodeToString(data[offset:end]),
},
}, wecomUploadTimeout)
if sendErr != nil {
return nil, sendErrView on GitHub (pinned to 49183d7e8d)
Solutions
- Retry the send after a short backoff — empty upload_id is usually transient
- Inspect the raw init ack (enable debug logging of the wecom channel) to confirm whether upload_id is truly absent vs. nested under a different key after a schema change
- If persistent, compare against the current WeCom upload protocol and update wecomUploadMediaInitResponse field tags
- Fall back to sending text-only content so the user gets a reply while media uploads are failing (the channel already does this for upload failures)
Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
func isWecomEmptyUploadID(err error) bool {
return err != nil && strings.Contains(err.Error(), "empty upload_id")
} Try / catch
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
if err := ch.Send(msg); err != nil {
if !isWecomEmptyUploadID(err) {
lastErr = err
break
}
time.Sleep(time.Duration(attempt+1) * 2 * time.Second)
lastErr = err
continue
}
lastErr = nil
break
} Prevention
- Retry transient upload-protocol errors with backoff; each retry gets a fresh upload_id
- Log the raw init ack when diagnosing repeated occurrences
- Keep attachments small so the whole upload cycle is short and less exposed to session glitches
When it happens
Trigger: Calling uploadOutboundMedia (i.e. sending any WeCom message with media) when the WeCom gateway acknowledges wecomCmdUploadMediaInit but omits upload_id in the decoded wecomUploadMediaInitResponse — server glitch, gateway version mismatch, or a truncated/malformed ack body that still passed envelope decoding.
Common situations: Transient WeCom service disruption during a burst of uploads; the WeCom WebSocket gateway was upgraded and changed the init ack schema; a proxy/middlebox rewrote or truncated the response body; extremely rare in steady state.
Related errors
- wecom upload finish returned empty media_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/50a91d45be6f2d18.
Report an issue: GitHub.