larksuite/cli · error

invalid EML: missing header/body separator

Error message

invalid EML: missing header/body separator

What it means

parseHeaderBlock splits the decoded EML at the first blank line (\n\n) separating headers from body. If no blank line exists, the bytes are not shaped like an RFC 5322 message (headers block followed by an empty line). The error indicates malformed or truncated EML content rather than a parser bug.

Source

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

		decoded, err := decode(raw)
		if err == nil {
			return normalizeLineEndings(decoded), nil
		}
	}
	return nil, fmt.Errorf("draft raw EML is not valid base64url")
}

func normalizeLineEndings(in []byte) []byte {
	in = bytes.ReplaceAll(in, []byte("\r\n"), []byte("\n"))
	in = bytes.ReplaceAll(in, []byte("\r"), []byte("\n"))
	return in
}

func parseHeaderBlock(raw []byte) ([]Header, []byte, error) {
	raw = normalizeLineEndings(raw)
	sep := bytes.Index(raw, []byte("\n\n"))
	if sep < 0 {
		return nil, nil, fmt.Errorf("invalid EML: missing header/body separator")
	}
	headerLines := strings.Split(string(raw[:sep]), "\n")
	headers := make([]Header, 0, len(headerLines))
	for _, line := range headerLines {
		if strings.TrimSpace(line) == "" {
			continue
		}
		if (strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t")) && len(headers) > 0 {
			headers[len(headers)-1].Value += " " + strings.TrimSpace(line)
			continue
		}
		name, value, ok := strings.Cut(line, ":")
		if !ok {
			// Skip lines without a colon rather than failing. Some email
			// systems insert comment or separator lines in the header area.
			continue
		}
		headers = append(headers, Header{

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Ensure the EML has a blank line (\\r\\n\\r\\n or \\n\\n) between the header block and the body
  2. Verify the payload decodes to real EML text (print the first ~200 bytes) before parsing
  3. If synthesizing EML, append \\r\\n\\r\\n after the last header before the body

Example fix

// before
raw := "From: a@b.com\r\nSubject: hi\r\nhello"
// after
raw := "From: a@b.com\r\nSubject: hi\r\n\r\nhello"
Defensive patterns

Strategy: validation

Validate before calling

decoded, err := decode(rawEML)
if err == nil && !bytes.Contains(decoded, []byte("\n\n")) && !bytes.Contains(decoded, []byte("\r\n\r\n")) {
    return errors.New("EML missing header/body blank line")
}

Try / catch

part, err := draft.Parse(rawEML)
if err != nil && strings.Contains(err.Error(), "missing header/body separator") {
    // dump first bytes of decoded payload for diagnosis; do not retry blind
    log.Printf("bad EML head: %q", decoded[:min(200, len(decoded))])
}

Prevention

When it happens

Trigger: Calling Parse with decoded bytes that have headers but no blank line before the body, empty body region glued directly to headers, a truncated EML, or binary garbage with no MIME/RFC822 structure.

Common situations: Manually constructing EML strings and forgetting the blank line, stripping blank lines with a text cleaner/regex, downloading a partial draft, or decoding a payload that was not actually an EML.

Related errors


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