sipeed/picoclaw · error
wecom response body is empty
Error message
wecom response body is empty
What it means
decodeWeComEnvelopeBody got an ack envelope with a zero-length Body (media.go:659-662). It is used for the chunked upload protocol's init and finish acks (media.go:710,747): sendCommandAck returned successfully, but the reply envelope carried no JSON payload to decode into wecomUploadMediaInitResponse/FinishResponse.
Source
Thrown at pkg/channels/wecom/media.go:662
func buildWeComVideoContent(mediaID, filename, description string) *wecomVideoContent {
title := strings.TrimSuffix(filename, filepath.Ext(filename))
title = trimWeComBytes(title, 64)
if title == "" {
title = "video"
}
description = trimWeComBytes(description, 512)
return &wecomVideoContent{
MediaID: mediaID,
Title: title,
Description: description,
}
}
func decodeWeComEnvelopeBody[T any](env wecomEnvelope) (T, error) {
var out T
if len(env.Body) == 0 {
return out, fmt.Errorf("wecom response body is empty")
}
if err := json.Unmarshal(env.Body, &out); err != nil {
return out, fmt.Errorf("decode wecom response body: %w", err)
}
return out, nil
}
func (c *WeComChannel) uploadOutboundMedia(
ctx context.Context,
localPath, filename, contentType string,
part bus.MediaPart,
) (*wecomOutboundMedia, error) {
_ = ctx
contentType = detectLocalWeComContentType(localPath, contentType)
filename = ensureWeComOutboundFilename(filename, localPath, contentType)
data, err := os.ReadFile(localPath)View on GitHub (pinned to 49183d7e8d)
Solutions
- Retry the upload once from scratch (new upload_id) - empty-body acks are usually transient
- If persistent: dump the raw envelope (cmd, req_id, body length) in debug logs and compare against the expected protocol
- Align versions of the channel and gateway/bridge components
- Check whether an error ack was sent separately and swallowed; correlate by ReqID
Example fix
// before: single attempt, empty body kills the send
initEnv, err := c.sendCommandAck(cmd, wecomUploadTimeout)
initResp, err := decodeWeComEnvelopeBody[wecomUploadMediaInitResponse](initEnv)
// after: one bounded retry on the empty-body case
var initResp wecomUploadMediaInitResponse
for attempt := 0; attempt < 2; attempt++ {
initEnv, err := c.sendCommandAck(cmd, wecomUploadTimeout)
if err != nil { return nil, err }
initResp, err = decodeWeComEnvelopeBody[wecomUploadMediaInitResponse](initEnv)
if err == nil || !strings.Contains(err.Error(), "body is empty") { break }
} Defensive patterns
Strategy: retry
Try / catch
var errEmptyBody = errors.New("wecom response body is empty")
// bounded single retry for transient empty-body acks
func uploadWithRetry(ctx context.Context, do func() error) error {
if err := do(); err == nil {
return nil
} else if !errors.Is(err, errEmptyBody) && !strings.Contains(err.Error(), "body is empty") {
return err
}
time.Sleep(500 * time.Millisecond)
return do()
} Prevention
- log ReqID with every upload command so acks can be correlated
- upgrade channel and gateway together
- treat empty-body acks as retriable, but alarm if the rate climbs
When it happens
Trigger: The WeCom gateway/bridge acknowledged the upload init (or finish) command but omitted the body: transient gateway hiccup, protocol/version drift where the server answers ack-only, or an error path that drops the payload while keeping the envelope.
Common situations: Upgrading one side of the picoclaw WeCom bridge without the other; gateway restarts mid-upload; custom gateways that never send bodies on certain commands.
Related errors
- decode wecom response body: %w
- wecom upload init returned empty upload_id
- wecom upload finish returned empty media_id
- failed to load config: %w
- decode JSON response: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/595fff6fd6121c57.
Report an issue: GitHub.