chenhg5/cc-connect · error

close multipart writer

Error message

close multipart writer: %w

What it means

multipart.Writer.Close() failed when finalizing the upload body for the DingTalk media API. Close flushes the closing boundary; failure here means the multipart body is incomplete and the request must not be sent.

Solutions

  1. Ensure Close is called exactly once, after all parts are written.
  2. If Close fails, abandon the body and rebuild it — a half-closed multipart body yields server-side parse errors.
  3. For large media, write to a temp file sink instead of an in-memory buffer to avoid allocation failures.

Example fix

// before
defer writer.Close()
...
req, _ := http.NewRequestWithContext(ctx, "POST", uploadURL, body) // may race Close
// after
if err := writer.Close(); err != nil { return "", fmt.Errorf("close multipart writer: %w", err) }
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadURL, body)
Defensive patterns

Strategy: try-catch

Try / catch

if err := writer.Close(); err != nil {
    return "", fmt.Errorf("close multipart writer: %w", err) // do NOT send the request
}

Prevention

When it happens

Trigger: writer.Close() returns an error in uploadMedia after all parts were written — typically an underlying buffer write failure or calling Close after the writer was already closed.

Common situations: Double-Close after a refactor; memory pressure on huge media bodies; custom io.Writer sink that errors on flush.

Related errors


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

Appendix: source

Thrown at platform/dingtalk/dingtalk.go:1360

		return "", fmt.Errorf("get access token: %w", err)
	}

	uploadURL := fmt.Sprintf("https://oapi.dingtalk.com/media/upload?access_token=%s&type=%s", token, mediaType)

	body := bytes.NewBuffer(nil)
	writer := multipart.NewWriter(body)

	part, err := writer.CreateFormFile("media", fileName)
	if err != nil {
		return "", fmt.Errorf("create form file: %w", err)
	}

	if _, err := part.Write(data); err != nil {
		return "", fmt.Errorf("write media data: %w", err)
	}

	if err := writer.Close(); err != nil {
		return "", fmt.Errorf("close multipart writer: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadURL, body)
	if err != nil {
		return "", fmt.Errorf("create upload request: %w", err)
	}

	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := p.httpClient.Do(req)
	if err != nil {
		return "", fmt.Errorf("upload request: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("read upload response: %w", err)

View on GitHub (pinned to 4000b2338a)