larksuite/cli · error

multipart part %s missing boundary

Error message

multipart part %s missing boundary

What it means

During RFC822/MIME parsing of a mail draft, parsePart encountered a part whose media type starts with 'multipart/' but whose Content-Type parameters contain no 'boundary' parameter. Without a boundary the multipart body cannot be split into child parts, so parsing aborts. This guards against malformed MIME structures.

Source

Thrown at shortcuts/mail/draft/parse.go:167

	} else {
		part.MediaParams["charset"] = "UTF-8"
	}
	if disp := headerValue(headers, "Content-Disposition"); disp != "" {
		dispType, params, err := mime.ParseMediaType(disp)
		if err == nil {
			part.ContentDisposition = strings.ToLower(dispType)
			part.ContentDispositionArg = lowerCaseKeys(params)
		}
		// On parse error, silently ignore the disposition. The original
		// header is preserved in part.Headers for serialization.
	}
	part.ContentID = strings.Trim(strings.TrimSpace(headerValue(headers, "Content-ID")), "<>")
	part.TransferEncoding = strings.ToLower(strings.TrimSpace(headerValue(headers, "Content-Transfer-Encoding")))

	if strings.HasPrefix(part.MediaType, "multipart/") {
		boundary := part.MediaParams["boundary"]
		if boundary == "" {
			return nil, fmt.Errorf("multipart part %s missing boundary", partID)
		}
		children, preamble, epilogue, err := parseMultipartChildren(body, boundary, partID, depth)
		if err != nil {
			return nil, err
		}
		if len(children) == 0 {
			// Boundary declared but never found in the body. Reclassify as
			// text rather than returning an empty multipart with no children
			// (following mail-parser's approach per Postel's law).
			part.MediaType = "text/plain"
			part.MediaParams = map[string]string{"charset": "UTF-8"}
			part.Body = body
			part.EncodingProblem = true
			return part, nil
		}
		part.Children = children
		part.Preamble = preamble
		part.Epilogue = epilogue

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Inspect the raw MIME source and ensure every multipart/* Content-Type header includes a boundary parameter, e.g. Content-Type: multipart/mixed; boundary="===BOUNDARY==="
  2. If you generate MIME yourself, use a MIME library (Go: mime/multipart with Writer.SetBoundary) instead of concatenating strings.
  3. If the MIME comes from a Lark draft, re-fetch the raw content and check it was not truncated or re-encoded in transit.
  4. Wrap the parse call and treat this as malformed input: reject or repair the payload before parsing.

Example fix

// before
Content-Type: multipart/mixed;
// after
Content-Type: multipart/mixed; boundary="----=_Part_0_123456"
Defensive patterns

Strategy: validation

Validate before calling

func hasBoundary(ct string) bool {
    _, params, _ := mime.ParseMediaType(ct)
    if !strings.HasPrefix(strings.ToLower(ct), "multipart/") { return true }
    return params["boundary"] != ""
}
// check before parsing: if !hasBoundary(contentType) { repair or reject }

Type guard

func isParseableMultipart(ct string) bool {
    if !strings.HasPrefix(strings.ToLower(ct), "multipart/") { return true }
    _, params, err := mime.ParseMediaType(ct)
    return err == nil && params["boundary"] != ""
}

Try / catch

part, err := parseRootPart(raw)
if err != nil {
    var e *errs.TypedError
    if errors.As(err, &e) { log.Printf("mime parse failed: %s", e.Message) }
    return fmt.Errorf("malformed draft MIME: %w", err)
}

Prevention

When it happens

Trigger: Parsing a draft whose raw MIME body contains a 'multipart/*' Content-Type header (e.g. multipart/mixed, multipart/alternative) with no 'boundary=' parameter, typically produced when parsing parts returned by parseRootPart or recursively via parseMultipartChildren.

Common situations: Feeding hand-crafted or third-party-generated MIME into the draft parser; upstream tools that strip Content-Type parameters; truncated or rewritten emails that lost the boundary parameter.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/6d46d80c1037d303. Report an issue: GitHub.