chenhg5/cc-connect · error

weixin: getUploadUrl json

Error message

weixin: getUploadUrl json: %w

What it means

getUploadURL requests a CDN upload address via ilink/bot/getuploadurl and decodes the reply into getUploadURLResponse. This error wraps a json.Unmarshal failure — the body is not valid JSON or does not fit the struct, typically an HTML error page or an API schema change.

Solutions

  1. Log the raw body (truncateForLog) to see what was returned
  2. Verify the bot session is valid and re-authenticate
  3. Update getUploadURLResponse to the current iLink schema
  4. Check for intermediaries (proxy/WAF) corrupting the response
Defensive patterns

Strategy: try-catch

Validate before calling

if len(bytes.TrimSpace(raw)) == 0 { return errors.New("weixin: empty getuploadurl body") }

Try / catch

resp, err := getUploadURL(ctx, req)
if err != nil {
	if strings.Contains(err.Error(), "getUploadUrl json") {
		slog.Error("weixin: upload-url response not JSON", "err", err)
		return fallbackUploadPath()
	}
	return err
}

Prevention

When it happens

Trigger: The getuploadurl endpoint returns non-JSON (proxy HTML, gateway error) or a new schema whose fields no longer decode into getUploadURLResponse (e.g. type changes causing UnmarshalTypeError).

Common situations: WeChat iLink API evolution (the code already works around upload_param→upload_full_url); proxies rewriting responses; authenticated session expired so the server returns an error page.

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/693bc186de0d60b4. Report an issue: GitHub.

Appendix: source

Thrown at platform/weixin/client.go:204

		return fmt.Errorf("weixin: sendMessage: ret=%d errcode=%d errmsg=%s",
			resp.Ret, resp.Errcode, resp.Errmsg)
	}
	return nil
}

func (c *apiClient) getUploadURL(ctx context.Context, req getUploadURLRequest) (*getUploadURLResponse, error) {
	req.BaseInfo = baseInfo{ChannelVersion: channelVersion}
	payload, err := json.Marshal(req)
	if err != nil {
		return nil, err
	}
	raw, err := c.post(ctx, "ilink/bot/getuploadurl", payload, 0, "getUploadUrl")
	if err != nil {
		return nil, err
	}
	var out getUploadURLResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return nil, fmt.Errorf("weixin: getUploadUrl json: %w", err)
	}
	// 兼容微信 iLink API 变更:新版返回 upload_full_url 而非 upload_param
	// upload_full_url 是完整的 CDN 上传地址,可独立作为成功路径
	if strings.TrimSpace(out.UploadParam) == "" && strings.TrimSpace(out.UploadFullURL) == "" {
		return nil, fmt.Errorf("weixin: getUploadUrl: empty upload_param and upload_full_url in %s", truncateForLog(raw, 512))
	}
	return &out, nil
}

func (c *apiClient) getConfig(ctx context.Context, userID, contextToken string) (*getConfigResp, error) {
	req := getConfigReq{
		UserID:       userID,
		ContextToken: contextToken,
		BaseInfo:     baseInfo{ChannelVersion: channelVersion},
	}
	payload, err := json.Marshal(req)
	if err != nil {
		return nil, err

View on GitHub (pinned to 4000b2338a)