{"record":{"id":"a379466b3ad183f5","repo":"quarkusio/quarkus","slug":"an-attachment-must-contain-either-a-file-or-a-raw","errorCode":null,"errorMessage":"An attachment must contain either a file or a raw data","messagePattern":"An attachment must contain either a file or a raw data","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MutinyMailerImpl.java","lineNumber":274,"sourceCode":"    private MultiMap toMultimap(Map<String, List<String>> headers) {\n        MultiMap mm = MultiMap.caseInsensitiveMultiMap();\n        headers.forEach(mm::add);\n        return mm;\n    }\n\n    private Uni<MailAttachment> toMailAttachment(Attachment attachment) {\n        MailAttachment attach = MailAttachment.create();\n        attach.setName(attachment.getName());\n        attach.setContentId(attachment.getContentId());\n        attach.setDescription(attachment.getDescription());\n        attach.setDisposition(attachment.getDisposition());\n        attach.setContentType(attachment.getContentType());\n\n        if ((attachment.getFile() == null && attachment.getData() == null) // No content\n                || (attachment.getFile() != null && attachment.getData() != null)) // Too much content\n        {\n\n            throw new IllegalArgumentException(\"An attachment must contain either a file or a raw data\");\n        }\n\n        return getAttachmentStream(vertx, attachment)\n                .onItem().transform(attach::setData);\n    }\n\n    private Recipients filterApprovedRecipients(List<String> emails) {\n        if (approvedRecipients.isEmpty()) {\n            return new Recipients(emails, List.of());\n        }\n\n        List<String> allowedRecipients = new ArrayList<>();\n        List<String> rejectedRecipients = new ArrayList<>();\n\n        emailLoop: for (String email : emails) {\n            for (Pattern approvedRecipient : approvedRecipients) {\n                if (approvedRecipient.matcher(email).matches()) {\n                    allowedRecipients.add(email);","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MutinyMailerImpl.java#L256-L292","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure each MailAttachment has exactly one content source: set either file(...) or data(...), never both","If you have bytes, use MailAttachment.builder().data(bytes) and do not also call file(...); if you have a path, use file(...) only","Before sending, check attachment.getFile() == null ^ attachment.getData() != null and skip or fix invalid attachments","If the attachment is built conditionally, add a guard that falls back to an empty/placeholder attachment when no content was produced"],"exampleFix":"// before\nMailAttachment att = MailAttachment.builder()\n        .fileName(\"report.pdf\")\n        .file(path)\n        .data(bytes) // both file and data set -> throws\n        .build();\n\n// after\nMailAttachment att = MailAttachment.builder()\n        .fileName(\"report.pdf\")\n        .data(bytes)\n        .build();","handlingStrategy":"validation","validationCode":"if ((att.getFile() == null && att.getData() == null) || (att.getFile() != null && att.getData() != null)) {\n    throw new IllegalArgumentException(\"Attachment must have exactly one of file or data: \" + att.getName());\n}","typeGuard":"static boolean hasExactlyOneContent(MailAttachment a) {\n    return (a.getFile() == null) ^ (a.getData() == null);\n}","tryCatchPattern":"try {\n    mailer.send(mail.addAttachment(att));\n} catch (IllegalArgumentException e) {\n    log.errorf(\"Invalid mail attachment: %s\", e.getMessage());\n}","preventionTips":["Use MailAttachment.builder() and set only one of file()/data() per attachment","Add a unit test asserting every attachment your code produces passes hasExactlyOneContent","When converting uploads, null out the source file path after reading bytes into data"],"tags":["quarkus","mailer","attachment","illegal-argument","validation"],"backgroundTag":"mail-attachment-content-invalid","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}