chenhg5/cc-connect · info
googlechat: upload: create media part: %w
Error message
googlechat: upload: create media part: %w
What it means
Returned by uploadAttachment when multipart.Writer.CreatePart fails while creating the media part for the uploaded file. CreatePart only fails on an invalid MIME header; since the header is just the provided mimeType string, this is practically unreachable with stock code unless mimeType contains illegal header characters. It surfaces via postAttachment from SendImage/SendFile.
Source
Thrown at platform/googlechat/googlechat.go:468
}
// uploadAttachment uploads raw bytes to the Chat media endpoint using a
// multipart/related request and returns the attachmentDataRef resource name.
func (p *Platform) uploadAttachment(ctx context.Context, space, filename, mimeType string, data []byte) (string, error) {
buf := bytes.NewBuffer(make([]byte, 0, 256+len(data)))
mw := multipart.NewWriter(buf)
metaPart, err := mw.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/json; charset=UTF-8"}})
if err != nil {
return "", fmt.Errorf("googlechat: upload: create metadata part: %w", err)
}
if err := json.NewEncoder(metaPart).Encode(map[string]string{"filename": filename}); err != nil {
return "", fmt.Errorf("googlechat: upload: encode metadata: %w", err)
}
mediaPart, err := mw.CreatePart(textproto.MIMEHeader{"Content-Type": {mimeType}})
if err != nil {
return "", fmt.Errorf("googlechat: upload: create media part: %w", err)
}
if _, err := mediaPart.Write(data); err != nil {
return "", fmt.Errorf("googlechat: upload: write media: %w", err)
}
if err := mw.Close(); err != nil {
return "", fmt.Errorf("googlechat: upload: finalize multipart body: %w", err)
}
uploadURL := chatUploadBase + space + "/attachments:upload?uploadType=multipart"
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadURL, buf)
if err != nil {
return "", fmt.Errorf("googlechat: upload: build request: %w", err)
}
req.Header.Set("Content-Type", "multipart/related; boundary="+mw.Boundary())
resp, err := p.doRequest(req)
if err != nil {
return "", errView on GitHub (pinned to 4000b2338a)
Solutions
- Sanitize or hardcode the mimeType (e.g. use mime.TypeByExtension or a fixed allowlist like image/png).
- Strip newlines/control characters from any dynamically computed mimeType before passing it in.
- Validate mimeType in config at startup if it comes from config.toml.
Example fix
// before
mime := cfg.MimeType // user-provided, may contain "\n"
// after
mime = strings.Map(func(r rune) rune {
if r < 32 || r == 127 { return -1 }
return r
}, cfg.MimeType) Defensive patterns
Strategy: validation
Validate before calling
// validate mimeType before calling SendFile/SendImage
mime := cfg.MimeType
if mime == "" || !strings.Contains(mime, "/") || strings.ContainsAny(mime, " \t\r\n\x00") {
return fmt.Errorf("invalid mimeType %q", mime)
} Type guard
func isValidMimeType(m string) bool {
parts := strings.SplitN(m, "/", 2)
return len(parts) == 2 && parts[0] != "" && parts[1] != "" &&
!strings.ContainsAny(m, " \t\r\n\x00")
} Prevention
- Derive mimeType from the file extension with mime.TypeByExtension instead of free-form config.
- Strip control characters and whitespace from any dynamic mimeType.
- Validate mimeType values in config.toml at startup.
- Use a fixed allowlist of upload MIME types where possible.
When it happens
Trigger: Calling SendImage()/SendFile() with a mimeType containing characters illegal in an HTTP header (e.g. newlines, control chars) can make CreatePart return an error.
Common situations: Config mistakes where mimeType is user-supplied or derived from an untrusted filename extension and contains whitespace/newlines.
Related errors
- googlechat: upload: create metadata part: %w
- googlechat: upload: encode metadata: %w
- googlechat: upload: write media: %w
- googlechat: upload: finalize multipart body: %w
- create form file: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/c18339ff1ba3517c.
Report an issue: GitHub.