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

  1. Log the raw initResp.Body (hex/string) at debug level to see exactly what the server returned.
  2. Check for a WeCom API/schema change and update the initBody struct tags to match the current response format.
  3. Handle structured server error responses (errcode/errmsg) explicitly before trying to decode upload_id.
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/4b1344bef7f8f88a. Report an issue: GitHub.