flowable/flowable-engine · error · FlowableMailException

Failed to add body part

Error message

Failed to add body part

What it means

Inside createMultiPartContent(), when attachments exist, an 'alternative' multipart holding the text/html body must be wrapped in a MimeBodyPart and added to the root multipart. If bodyPart.setContent(...) or rootContainer.addBodyPart(...) throws MessagingException, it is wrapped as FlowableMailException "Failed to add body part".

Solutions

  1. Read the nested MessagingException 'ex' cause for the actual failure reason
  2. Verify the jakarta.mail (Angus Mail) implementation is up to date and consistent
  3. Reproduce with a simple text+one-attachment email to isolate the failing part
  4. If custom mail properties are set (mail.mime.*), test removing them

Example fix

// before: conflicting custom MIME properties
properties.put("mail.mime.encodeparameters", "false");
properties.put("mail.mime.decodeparameters", "true");
// after: remove conflicting overrides and rely on defaults
// (no mail.mime.* overrides)
Defensive patterns

Strategy: try-catch

Try / catch

try {
    mailClient.send(mailMessage);
} catch (FlowableMailException e) {
    if (e.getMessage().contains("Failed to add body part")) {
        log.error("Nested multipart assembly failed; review mail.mime.* overrides and provider", e.getCause());
    }
}

Prevention

When it happens

Trigger: Sending an email with BOTH a text/html body AND attachments, where attaching the nested body container to the root multipart fails with MessagingException.

Common situations: Jakarta Mail provider-level failures when nesting multiparts; corrupted session/properties state; rarely hit — usually points to a broken mail provider setup or a header write failure on the composed part.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/16b76e29d42d9bbb. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-mail/src/main/java/org/flowable/mail/common/impl/jakarta/mail/JakartaMailFlowableMailClient.java:293

    protected MimeMultipart createMultiPartContent(String text, String html, String charset, Collection<DataSource> attachments) throws MessagingException {
        boolean attachmentsExists = attachments != null && !attachments.isEmpty();

        MimeMultipart rootContainer = new MimeMultipart();
        MimeMultipart bodyContainer = rootContainer;

        rootContainer.setSubType("mixed");

        if (StringUtils.isNotEmpty(text) && StringUtils.isNotEmpty(html)) {
            if (attachmentsExists) {
                // If both HTML and TEXT bodies are provided, create an alternative
                // container and add it to the root container
                bodyContainer = new MimeMultipart("alternative");
                MimeBodyPart bodyPart = new MimeBodyPart();
                try {
                    bodyPart.setContent(bodyContainer);
                    rootContainer.addBodyPart(bodyPart);
                } catch (MessagingException ex) {
                    throw new FlowableMailException("Failed to add body part", ex);
                }
            } else {
                // no attachments present, change the mimetype
                // of the root container (= body container)
                rootContainer.setSubType("alternative");
            }
        }

        if (StringUtils.isNotEmpty(html)) {
            MimeBodyPart msgHtml = new MimeBodyPart();
            bodyContainer.addBodyPart(msgHtml, 0);
            msgHtml.setText(html, charset, "html");

            String contentType = msgHtml.getContentType();
            if (contentType == null || !contentType.equals(CONTENT_TYPE_TEXT_HTML)) {
                if (StringUtils.isNotEmpty(charset)) {
                    msgHtml.setContent(html, CONTENT_TYPE_TEXT_HTML + "; charset=" + charset);
                } else {

View on GitHub (pinned to d6d39ce1c6)