quarkusio/quarkus · error · IllegalArgumentException

An attachment must contain either a file or a raw data

Error message

An attachment must contain either a file or a raw data

What it means

The Quarkus mailer refuses to build a mail attachment whose content is ambiguous or missing. An attachment must carry exactly one content source: a file on disk OR raw byte data, never both and never neither. MutinyMailerImpl.toMailAttachment validates this before converting the MailAttachment to a Vert.x attachment stream and throws IllegalArgumentException immediately when the rule is violated.

Source

Thrown at extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MutinyMailerImpl.java:274

    private MultiMap toMultimap(Map<String, List<String>> headers) {
        MultiMap mm = MultiMap.caseInsensitiveMultiMap();
        headers.forEach(mm::add);
        return mm;
    }

    private Uni<MailAttachment> toMailAttachment(Attachment attachment) {
        MailAttachment attach = MailAttachment.create();
        attach.setName(attachment.getName());
        attach.setContentId(attachment.getContentId());
        attach.setDescription(attachment.getDescription());
        attach.setDisposition(attachment.getDisposition());
        attach.setContentType(attachment.getContentType());

        if ((attachment.getFile() == null && attachment.getData() == null) // No content
                || (attachment.getFile() != null && attachment.getData() != null)) // Too much content
        {

            throw new IllegalArgumentException("An attachment must contain either a file or a raw data");
        }

        return getAttachmentStream(vertx, attachment)
                .onItem().transform(attach::setData);
    }

    private Recipients filterApprovedRecipients(List<String> emails) {
        if (approvedRecipients.isEmpty()) {
            return new Recipients(emails, List.of());
        }

        List<String> allowedRecipients = new ArrayList<>();
        List<String> rejectedRecipients = new ArrayList<>();

        emailLoop: for (String email : emails) {
            for (Pattern approvedRecipient : approvedRecipients) {
                if (approvedRecipient.matcher(email).matches()) {
                    allowedRecipients.add(email);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Ensure each MailAttachment has exactly one content source: set either file(...) or data(...), never both
  2. If you have bytes, use MailAttachment.builder().data(bytes) and do not also call file(...); if you have a path, use file(...) only
  3. Before sending, check attachment.getFile() == null ^ attachment.getData() != null and skip or fix invalid attachments
  4. If the attachment is built conditionally, add a guard that falls back to an empty/placeholder attachment when no content was produced

Example fix

// before
MailAttachment att = MailAttachment.builder()
        .fileName("report.pdf")
        .file(path)
        .data(bytes) // both file and data set -> throws
        .build();

// after
MailAttachment att = MailAttachment.builder()
        .fileName("report.pdf")
        .data(bytes)
        .build();
Defensive patterns

Strategy: validation

Validate before calling

if ((att.getFile() == null && att.getData() == null) || (att.getFile() != null && att.getData() != null)) {
    throw new IllegalArgumentException("Attachment must have exactly one of file or data: " + att.getName());
}

Type guard

static boolean hasExactlyOneContent(MailAttachment a) {
    return (a.getFile() == null) ^ (a.getData() == null);
}

Try / catch

try {
    mailer.send(mail.addAttachment(att));
} catch (IllegalArgumentException e) {
    log.errorf("Invalid mail attachment: %s", e.getMessage());
}

Prevention

When it happens

Trigger: Calling any mailer send method with a MailAttachment where both getFile() and getData() are null (no content set), or where both are non-null (file and data set simultaneously). Typically caused by building a MailAttachment via builder without calling either file(...) or data(...), or setting data programmatically while file(...) was already set.

Common situations: Building attachments dynamically from uploaded bytes and forgetting to clear the source file path; conditional code where neither branch executed so the builder produced an empty attachment; loading file bytes into data but leaving the file field populated; deserializing attachment config from JSON where only one field was expected but both were present.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/a379466b3ad183f5. Report an issue: GitHub.