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
- Check call ordering in uploadMedia: create form file → write data → then Close, exactly once.
- Do not share the multipart.Writer between goroutines; build one body per upload.
- 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
- Track writer lifecycle with a closed flag or single function scope.
- Never call CreateFormFile after Close; keep build order: parts → data → Close.
- One multipart.Writer per request; no sharing across goroutines.
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
- write media data: %w
- close multipart writer: %w
- dingtalk: upload image: %w
- dingtalk: upload audio: %w
- create upload request: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/67742041647cd700.
Report an issue: GitHub.