anomalyco/sst · error
failed to create form part %s: %w
Error message
failed to create form part %s: %w
What it means
uploadAssets builds a multipart/form-data body for Cloudflare's Workers Assets upload API. This error wraps a failure from mime/multipart Writer.CreatePart, which fails only if the multipart writer's boundary could not be generated or the header map is malformed (e.g. nil/empty header values). It is nearly always a symptom of the internal writer being in a bad state rather than user input.
Source
Thrown at pkg/server/resource/cloudflare-worker-assets.go:249
}
// Read file content
absFilePath := filepath.Join(directory, fileKey)
fileContent, err := os.ReadFile(absFilePath)
if err != nil {
return "", fmt.Errorf("failed to read file %s: %w", absFilePath, err)
}
// Base64 encode the file content
encodedContent := base64.StdEncoding.EncodeToString(fileContent)
// Create form field with content type header
part, err := writer.CreatePart(map[string][]string{
"Content-Disposition": []string{fmt.Sprintf(`form-data; name="%s"; filename="%s"`, hash, hash)},
"Content-Type": []string{contentType},
})
if err != nil {
return "", fmt.Errorf("failed to create form part %s: %w", hash, err)
}
_, err = part.Write([]byte(encodedContent))
if err != nil {
return "", fmt.Errorf("failed to write encoded content for %s: %w", hash, err)
}
}
err := writer.Close()
if err != nil {
return "", fmt.Errorf("failed to close writer: %w", err)
}
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/accounts/%s/workers/assets/upload?base64=true", accountId)
req, err := http.NewRequest("POST", url, &body)
if err != nil {
return "", err
}View on GitHub (pinned to a0bd20f762)
Solutions
- Verify every asset has a non-empty hash and contentType before calling uploadAssets
- Recreate the multipart.Writer if it was previously closed or reused
- Check the wrapped cause (%w) with errors.Unwrap to see the underlying mime error
- Upgrade/patch the code to skip assets with empty hash/contentType instead of building a part
Example fix
// before
part, err := writer.CreatePart(map[string][]string{
"Content-Disposition": []string{fmt.Sprintf(`form-data; name="%s"; filename="%s"`, hash, hash)},
"Content-Type": []string{contentType},
})
// after
if hash == "" || contentType == "" {
return "", fmt.Errorf("invalid asset: hash=%q contentType=%q", hash, contentType)
}
part, err := writer.CreatePart(map[string][]string{
"Content-Disposition": []string{fmt.Sprintf(`form-data; name=%q; filename=%q`, hash, hash)},
"Content-Type": []string{contentType},
}) Defensive patterns
Strategy: validation
Validate before calling
for hash, contentType := range assets {
if hash == "" || contentType == "" {
return fmt.Errorf("invalid asset entry: hash=%q contentType=%q", hash, contentType)
}
} Type guard
func validAsset(hash, contentType string) bool {
return hash != "" && contentType != ""
} Try / catch
part, err := writer.CreatePart(headers)
if err != nil {
return "", fmt.Errorf("failed to create form part %s: %w", hash, err)
} Prevention
- Validate hash and contentType are non-empty before building parts
- Never reuse a multipart.Writer after Close
- Unwrap and log the underlying mime error for diagnosis
When it happens
Trigger: Writer.CreatePart returns non-nil err while iterating asset hashes in uploadAssets (pkg/server/resource/cloudflare-worker-assets.go:249); in practice this happens only when the multipart.Writer is corrupted or the Content-Disposition/Content-Type header map yields invalid MIME headers (e.g. empty hash or contentType producing a malformed header value).
Common situations: An asset with an empty hash or empty contentType string reaching the loop; programmatic misuse after the writer was already closed; extremely rare mime/multipart edge cases with non-ASCII header values.
Related errors
- failed to close writer: %w
- failed to complete asset upload - no completion JWT received
- failed to write encoded content for %s: %w
- failed to upload assets: HTTP %d %s
- bucket %v upload failed: %w
AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30).
Data as JSON: /api/errors/30e4758f5bfd2cee.
Report an issue: GitHub.