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

  1. Verify every asset has a non-empty hash and contentType before calling uploadAssets
  2. Recreate the multipart.Writer if it was previously closed or reused
  3. Check the wrapped cause (%w) with errors.Unwrap to see the underlying mime error
  4. 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

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


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/30e4758f5bfd2cee. Report an issue: GitHub.