larksuite/cli · error
MIME nesting too deep (max %d levels)
Error message
MIME nesting too deep (max %d levels)
What it means
parsePart recurses into nested MIME entities (multipart/* and message/rfc822) and enforces maxMIMEDepth of 50 levels. Exceeding it means the message tree is pathologically nested — either genuinely crafted or cyclic/malformed. The parser aborts to bound recursion and memory rather than risk stack exhaustion.
Source
Thrown at shortcuts/mail/draft/parse.go:127
Headers: append([]Header{}, partHeaders...),
}
if len(partHeaders) == 0 {
part.MediaType = "text/plain"
part.MediaParams = map[string]string{"charset": "UTF-8"}
part.TransferEncoding = "7bit"
part.Body = body
part.RawEntity = append([]byte{}, body...)
return part, nil
}
rawEntity := buildRawEntity(filterRawEntityHeaders(partHeaders), body)
return parsePart(partHeaders, body, "1", rawEntity, 0)
}
const maxMIMEDepth = 50
func parsePart(headers []Header, body []byte, partID string, rawEntity []byte, depth int) (*Part, error) {
if depth > maxMIMEDepth {
return nil, fmt.Errorf("MIME nesting too deep (max %d levels)", maxMIMEDepth)
}
part := &Part{
PartID: partID,
Headers: append([]Header{}, headers...),
MediaType: "text/plain",
MediaParams: map[string]string{},
ContentDispositionArg: map[string]string{},
RawEntity: append([]byte{}, rawEntity...),
}
if ct := headerValue(headers, "Content-Type"); ct != "" {
mediaType, params, err := mime.ParseMediaType(ct)
if err != nil {
// Fallback: treat as opaque binary so the part is still accessible
// and can round-trip through RawEntity. The original Content-Type
// header is preserved for serialization.
part.MediaType = "application/octet-stream"
part.EncodingProblem = true
} else {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Flatten the message before parsing: unwrap redundant multipart layers or re-emit the EML with a shallower structure
- Inspect the decoded EML's Content-Type tree to find what generates the deep nesting
- If legitimately deep messages are common, extract top-level parts separately instead of full recursive parse; do not raise maxMIMEDepth, it guards against stack exhaustion
Example fix
// before
part, err := draft.Parse(rawEML) // errors on deeply nested MIME
// after
if depth, ok := mimeDepthOf(rawEML); ok && depth > 50 {
return fmt.Errorf("refusing to parse: MIME nesting depth %d exceeds limit 50", depth)
}
part, err := draft.Parse(rawEML) Defensive patterns
Strategy: try-catch
Try / catch
part, err := draft.Parse(rawEML)
if err != nil && strings.Contains(err.Error(), "MIME nesting too deep") {
// treat message as untrusted: quarantine, notify sender/owner, skip parsing
return fmt.Errorf("draft rejected: malformed or malicious MIME nesting: %w", err)
} Prevention
- Treat >50-level nesting as hostile; never loosen the parser limit
- When re-forwarding or re-wrapping messages programmatically, flatten instead of nesting
- Log the draft ID and Content-Type chain when this triggers to find the nesting source
When it happens
Trigger: Parsing a decoded EML whose MIME part nesting exceeds 50 levels: e.g. deeply nested multipart/mixed>alternative>related>... chains or message/rfc822 attachments recursively embedding messages.
Common situations: Maliciously crafted emails (mailbomb nesting), automation that repeatedly wraps/forwards messages (each forward adds a multipart layer), or a bug in code that regenerates attachments as nested multiparts.
Related errors
- attachment part %q not found
- part %q is an inline MIME part; use remove_inline
- no part with cid %q found
- inline cid is empty
- inline cid %q contains invalid characters (spaces, tabs, ang
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/9575f074adc5b589.
Report an issue: GitHub.