chenhg5/cc-connect · error

dingtalk: upload image: %w

Error message

dingtalk: upload image: %w

What it means

This error is returned by Platform.SendImage when p.uploadMedia fails — the step that uploads the image bytes to DingTalk's media API to obtain a mediaID before sending the oToMessages image message. The wrapped err contains the underlying cause: HTTP failure, non-2xx response from the media endpoint, invalid file, or authentication problems. Without a mediaID the image message cannot be constructed, so SendImage aborts.

Source

Thrown at platform/dingtalk/dingtalk.go:1027

	}
}

// SendImage uploads and sends an image via DingTalk oToMessages API.
// Implements core.ImageSender.
func (p *Platform) SendImage(ctx context.Context, rctx any, img core.ImageAttachment) error {
	rc, ok := rctx.(replyContext)
	if !ok {
		return fmt.Errorf("dingtalk: SendImage: invalid reply context type %T", rctx)
	}

	name := img.FileName
	if name == "" {
		name = "image.png"
	}

	mediaID, err := p.uploadMedia(ctx, img.Data, name, "image")
	if err != nil {
		return fmt.Errorf("dingtalk: upload image: %w", err)
	}

	slog.Debug("dingtalk: image uploaded", "media_id", mediaID, "size", len(img.Data))

	token, err := p.getAccessToken()
	if err != nil {
		return fmt.Errorf("dingtalk: get access token: %w", err)
	}

	msgParamBytes, _ := json.Marshal(map[string]string{"photoURL": mediaID})
	requestBody := map[string]any{
		"robotCode": p.robotCode,
		"userIds":   []string{rc.senderStaffId},
		"msgKey":    "sampleImageMsg",
		"msgParam":  string(msgParamBytes),
	}

	body, err := json.Marshal(requestBody)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check the wrapped error for the actual upload failure (status code / errcode from the media API)
  2. Verify the image size is within DingTalk's media upload limit (compress or downscale large images)
  3. Confirm img.Data is non-empty and the file is a valid image with a supported extension
  4. Check network access to DingTalk's media upload endpoint from the host
  5. Retry — transient upload failures are common on flaky networks

Example fix

// before: sending a 25MB screenshot
img := core.ImageAttachment{Data: bigScreenshot, FileName: "shot.png"}
err := p.SendImage(ctx, rc, img)
// after: downscale/compress before sending
if len(img.Data) > dingTalkMediaMaxSize {
    img.Data = compressImage(img.Data)
}
err := p.SendImage(ctx, rc, img)
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate the image before uploading
if len(img.Data) == 0 {
    return errors.New("sendImage: empty image data")
}
const maxDingTalkMedia = 20 << 20 // 20MB
if len(img.Data) > maxDingTalkMedia {
    return fmt.Errorf("sendImage: image %d bytes exceeds limit", len(img.Data))
}

Try / catch

if err := p.SendImage(ctx, rctx, img); err != nil {
    if strings.Contains(err.Error(), "upload image") {
        // check wrapped cause: size limit, bad media, or network
        slog.Warn("dingtalk media upload failed, skipping image",
            "err", err, "size", len(img.Data))
        return sendTextFallback(ctx, rctx, "[image could not be delivered]")
    }
    return err
}

Prevention

When it happens

Trigger: uploadMedia returns an error during SendImage — media endpoint unreachable, DingTalk rejects the upload (file too large, unsupported media type "image", invalid token), or the multipart upload itself fails.

Common situations: Image exceeds DingTalk's media size limit (e.g. very large screenshots pasted from chat); image data corrupted or empty; access token invalid at upload time; firewall blocking the media upload domain.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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