chenhg5/cc-connect · error

create form file: %w

Error message

create form file: %w

What it means

multipart.Writer.CreateFormFile failed while building the multipart body for the DingTalk media upload. This is rare — it only errors when the writer has already been closed or its internal boundary is malformed — and it indicates a programming/state bug rather than a network or server issue.

Source

Thrown at platform/dingtalk/dingtalk.go:1352

	return stdout.Bytes(), "mp3", nil
}

// uploadMedia uploads a file to DingTalk media API and returns the media ID.
// mediaType should be "voice" or "image".
func (p *Platform) uploadMedia(ctx context.Context, data []byte, fileName, mediaType string) (string, error) {
	token, err := p.getAccessToken()
	if err != nil {
		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)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check call ordering in uploadMedia: create form file → write data → then Close, exactly once.
  2. Do not share the multipart.Writer between goroutines; build one body per upload.
  3. If you added custom parts before/after, ensure no part creation happens after Close.

Example fix

// before
writer.Close()
part, err := writer.CreateFormFile("media", fileName) // too late
// after
part, err := writer.CreateFormFile("media", fileName)
// ... write data ...
writer.Close()
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure writer state before creating the form file
select {
case <-writerClosed:
    return errors.New("multipart writer already closed")
default:
}

Type guard

func writerOpen(w *multipart.Writer, closed *atomic.Bool) bool { return !closed.Load() }

Prevention

When it happens

Trigger: Calling writer.CreateFormFile("media", fileName) after the writer was closed or the underlying buffer state is invalid during uploadMedia.

Common situations: Refactored upload code that closes the writer before writing all parts; reuse of a single multipart.Writer across concurrent uploads; fileName containing pathological characters is NOT a cause here (CreateFormFile escapes only the filename into the header, it does not validate).

Related errors


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