chenhg5/cc-connect · error
decode upload init response: %w
Error message
decode upload init response: %w
What it means
uploadWSMedia received the aibot_upload_media_init ack but its Body could not be parsed as JSON into the expected {upload_id: string} shape. json.Unmarshal fails on invalid JSON or on a shape mismatch (e.g. upload_id is not a string), and the parse error is wrapped as "decode upload init response: %w".
Source
Thrown at platform/wecom/websocket_outbound_media.go:74
"cmd": "aibot_upload_media_init",
"headers": map[string]string{"req_id": initReqID},
"body": map[string]any{
"type": mediaType,
"filename": filename,
"total_size": len(data),
"total_chunks": totalChunks,
"md5": hex.EncodeToString(sum[:]),
},
}
initResp, err := p.writeAndWaitFrameWithTimeout(ctx, initFrame, initReqID, wsMediaAckTimeout)
if err != nil {
return "", fmt.Errorf("upload init: %w", err)
}
var initBody struct {
UploadID string `json:"upload_id"`
}
if err := json.Unmarshal(initResp.Body, &initBody); err != nil {
return "", fmt.Errorf("decode upload init response: %w", err)
}
if initBody.UploadID == "" {
return "", fmt.Errorf("upload init: empty upload_id")
}
for i := 0; i < totalChunks; i++ {
start := i * wecomWSUploadChunkSize
end := start + wecomWSUploadChunkSize
if end > len(data) {
end = len(data)
}
reqID := p.generateReqID("aibot_upload_media_chunk")
chunkFrame := map[string]any{
"cmd": "aibot_upload_media_chunk",
"headers": map[string]string{"req_id": reqID},
"body": map[string]any{
"upload_id": initBody.UploadID,
"chunk_index": i,View on GitHub (pinned to 4000b2338a)
Solutions
- Log the raw initResp.Body (hex/string) at debug level to see exactly what the server returned.
- Check for a WeCom API/schema change and update the initBody struct tags to match the current response format.
- Handle structured server error responses (errcode/errmsg) explicitly before trying to decode upload_id.
- Confirm the frame you're decoding is the ack to your init request (request-ID routing) and not an unrelated frame.
Example fix
// before
var initBody struct {
UploadID string `json:"upload_id"`
}
if err := json.Unmarshal(initResp.Body, &initBody); err != nil {
return "", fmt.Errorf("decode upload init response: %w", err)
}
// after
var initBody struct {
UploadID string `json:"upload_id"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if err := json.Unmarshal(initResp.Body, &initBody); err != nil {
return "", fmt.Errorf("decode upload init response (body=%q): %w", string(initResp.Body), err)
}
if initBody.ErrCode != 0 {
return "", fmt.Errorf("upload init rejected: errcode=%d errmsg=%s", initBody.ErrCode, initBody.ErrMsg)
} Defensive patterns
Strategy: type-guard
Validate before calling
func looksLikeJSON(b []byte) bool {
t := bytes.TrimSpace(b)
return len(t) > 0 && (t[0] == '{' || t[0] == '[')
}
// check initResp.Body before unmarshalling; log raw body if not JSON Type guard
func isInitAck(body []byte) (uploadID string, ok bool) {
var v struct {
UploadID any `json:"upload_id"`
}
if json.Unmarshal(body, &v) != nil {
return "", false
}
s, isStr := v.UploadID.(string)
return s, isStr && s != ""
} Try / catch
if err := json.Unmarshal(initResp.Body, &initBody); err != nil {
slog.Error("wecom upload init: unparseable ack", "body", string(initResp.Body), "err", err)
return "", fmt.Errorf("decode upload init response: %w", err)
} Prevention
- Always log raw response bodies at debug level for upload handshakes.
- Model error responses (errcode/errmsg) in your structs so rejections parse cleanly.
- Pin and test against specific WeCom API versions; watch changelogs for schema changes.
- Validate frame routing (request-ID match) before decoding bodies.
When it happens
Trigger: SendImage/SendFile when the WeCom server returns malformed JSON, an HTML/error page, a binary frame, or a JSON body whose upload_id field has a non-string type (null, number, object) instead of the expected string.
Common situations: WeCom API version change altering the response schema; server returning an error payload (e.g. {"errcode":...,"errmsg":...} with different field types) that doesn't match the struct; gateway/proxy injecting non-JSON content; truncated frames.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- qq: invalid API response
- wecom-ws: send image: %w
- upload init: %w
- upload init: empty upload_id
- wecom-ws: ack timeout
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/4b1344bef7f8f88a.
Report an issue: GitHub.