π¨ PDF Attachment - Immediate Fix Needed
Date: November 5, 2025, 1:00 PM
Critical Issue: Attachments are being lost between queue and send
User Feedback: "Queue works immediately"
π― THE SITUATIONβ
User confirmed: Queue processes immediately (not waiting)
This means:
- β Queue is working
- β Jobs process instantly
- β But attachments are still not being sent!
π THE BUGβ
Since queue works immediately, the attachment content is being lost during the queue serialize/deserialize cycle.
What Happens:β
1. Enqueue called with payload containing:
{
attachments: [{
filename: "invoice.pdf",
content: "JVBERi0x..." (65KB base64 string),
mimeType: "application/pdf"
}]
}
2. Queue stores payload in database as JSON/JSONB
β Payload might be too large
β OR attachments are being excluded
3. Queue immediately retrieves payload
β Attachments might be missing
β OR content field is empty/null
4. executeSend() receives payload
β NO attachments or empty content!
5. Email adapter sends without attachments
β Empty attachment in email!
π§ THE SOLUTIONβ
We Need To:β
STOP storing attachment content in the queue payload!
Instead:
- β
Save attachment metadata to
communicationAttachmenttable - β ALSO save the base64 content to a new column
- β Enqueue WITHOUT attachments (too large)
- β
In
executeSend(), fetch attachments FROM database - β Send email with retrieved content
π― IMPLEMENTATION PLANβ
Step 1: Add Content Column to Database (1 min)β
Migration needed:
ALTER TABLE communication_attachment
ADD COLUMN content TEXT;
Step 2: Save Content When Saving Attachments (2 min)β
Update AttachmentHandlerService.saveAttachments():
await this.database
.insertInto("communicationAttachment")
.values({
communicationId,
filename: attachment.filename,
filePath: undefined,
fileUrl: attachment.fileUrl,
mimeType: attachment.mimeType,
sizeBytes: attachment.content ? Buffer.from(attachment.content, "base64").length : undefined,
content: attachment.content, // β ADD THIS!
createdAt: undefined,
})
.execute();
Step 3: Exclude Attachments from Queue Payload (1 min)β
Update CommunicationsService.send():
// Don't include attachments in queue (too large)
const { attachments, ...payloadWithoutAttachments } = data;
await this.queueService.enqueue({
communicationId: communication.id,
payload: {
...payloadWithoutAttachments, // β No attachments!
content,
subject,
providerTemplateId,
communicationId: communication.id,
},
...
});
Step 4: Retrieve Attachments in executeSend() (3 min)β
Update CommunicationsService.executeSend():
async executeSend(
payload: SendCommunicationDto & { communicationId: string },
): Promise<void> {
try {
// Fetch attachments from database (not in payload)
const dbAttachments = await this.attachmentHandler.getAttachments(
payload.communicationId,
);
if (dbAttachments && dbAttachments.length > 0) {
// Convert DB format to DTO format
payload.attachments = dbAttachments.map(att => ({
filename: att.filename,
content: att.content, // From database!
mimeType: att.mimeType,
}));
this.logger.log(
`Retrieved ${payload.attachments.length} attachment(s) from database`
);
}
// Send via adapter
const adapter = this.channelFactory.getAdapter(payload.channel);
const result = await adapter.send(payload);
...
}
}
β±οΈ TIME ESTIMATEβ
- Database migration: 1 minute
- Code updates: 5 minutes
- Testing: 2 minutes
- Total: ~8 minutes β
π EXPECTED RESULTβ
After fix:
- β Attachments saved to database WITH content
- β Queue payload is smaller (no large base64)
- β executeSend retrieves attachments from DB
- β Email sent with REAL PDF attachment!
π NEXT STEPSβ
- Create database migration
- Update
AttachmentHandlerService - Update
CommunicationsService.send() - Update
CommunicationsService.executeSend() - Restart backend
- Test!
Ready to implement this fix? π