larksuite/cli · error
draft raw EML is not valid base64url
Error message
draft raw EML is not valid base64url
What it means
decodeRawEML expects the EML as base64url text (it tries standard and raw variants of URL and Std alphabets, then falls back to treating input as raw EML bytes elsewhere). If every decoder fails, the input is neither valid base64 nor was it pre-decoded. The error aggregates all decoder failures into one message.
Source
Thrown at shortcuts/mail/draft/parse.go:66
if raw == "" {
return nil, fmt.Errorf("draft raw EML is empty")
}
if len(raw) > maxRawEMLSize {
return nil, fmt.Errorf("draft raw EML is too large (%d bytes, max %d)", len(raw), maxRawEMLSize)
}
decoders := []func(string) ([]byte, error){
base64.URLEncoding.DecodeString,
base64.RawURLEncoding.DecodeString,
base64.StdEncoding.DecodeString,
base64.RawStdEncoding.DecodeString,
}
for _, decode := range decoders {
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) == "" {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Ensure the input is base64url-encoded (or try base64.StdEncoding first to check); decode/encode explicitly at your boundary
- Strip whitespace/newlines before encoding: base64.URLEncoding.EncodeString([]byte(emlText))
- Log decode errors per-variant locally to identify alphabet/padding issues
Example fix
// before part, err := draft.Parse(emlPlainText) // after encoded := base64.URLEncoding.EncodeString([]byte(emlPlainText)) part, err := draft.Parse(encoded)
Defensive patterns
Strategy: validation
Validate before calling
if _, err := base64.StdEncoding.DecodeString(strings.TrimSpace(rawEML)); err != nil {
if _, err2 := base64.RawURLEncoding.DecodeString(rawEML); err2 != nil {
return fmt.Errorf("raw EML is not base64: encode it before calling Parse")
}
} Type guard
func isBase64(s string) bool {
for _, enc := range []*base64.Encoding{
base64.URLEncoding, base64.RawURLEncoding,
base64.StdEncoding, base64.RawStdEncoding,
} {
if _, err := enc.DecodeString(strings.TrimSpace(s)); err == nil {
return true
}
}
return false
} Try / catch
part, err := draft.Parse(rawEML)
if err != nil && strings.Contains(err.Error(), "not valid base64url") {
encoded := base64.URLEncoding.EncodeString([]byte(rawEML))
part, err = draft.Parse(encoded)
} Prevention
- Encode EML text with base64.URLEncoding.EncodeString immediately before Parse
- Never pass plaintext EML directly into APIs documented as taking encoded content
- Centralize encode-then-parse in one helper to avoid double-encoding mistakes
When it happens
Trigger: Passing already-decoded EML text (starting with 'Message-ID:' etc.) directly to Parse, passing base64 with an unsupported alphabet/whitespace/URL-safe mixing beyond the four decoders, or truncated base64 with wrong padding.
Common situations: Confusing the raw EML body with its encoded form after an API change, copying base64 with line breaks the decoder cannot tolerate in that variant, or double-encoding/decoding in a pipeline.
Related errors
- add_attachment requires path
- remove_attachment requires target with at least one of part_
- add_inline requires path
- add_inline requires cid
- replace_inline requires target with at least one of part_id
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/d68851c97714f4f1.
Report an issue: GitHub.