payloadcms/payload · error · APIError
Attachment is missing both content and path
Error message
Attachment is missing both content and path
What it means
Thrown by the Resend email provider's attachment mapper when an attachment object has a filename but neither a `content` value nor a `path`. The mapper can only forward an attachment to the Resend API if it has bytes to send, so an attachment that is filename-only is rejected as malformed input (HTTP 400). This is a client-side validation gate before the Resend HTTP call is ever made.
Source
Thrown at packages/email-resend/src/index.ts:156
}
return [addresses.address]
}
function mapAttachments(
attachments: SendEmailOptions['attachments'],
): ResendSendEmailOptions['attachments'] {
if (!attachments) {
return []
}
return attachments.map((attachment): Attachment => {
if (!attachment.filename) {
throw new APIError('Attachment is missing filename', 400)
}
if (!attachment.content && !attachment.path) {
throw new APIError('Attachment is missing both content and path', 400)
}
// When both content and path are provided, content takes priority; path is ignored.
if (attachment.path && !attachment.content) {
const path = typeof attachment.path === 'string' ? attachment.path : attachment.path.href
return {
filename: attachment.filename,
path,
}
}
if (typeof attachment.content === 'string') {
return {
content: attachment.content,
filename: attachment.filename,
}
}
View on GitHub (pinned to 00c58b35c0)
Solutions
- Supply `path` (a file path, URL string, or URL object) OR `content` (string or Buffer) on every attachment: `attachments: [{ filename: 'x.pdf', path: '/tmp/x.pdf' }]`.
- Filter out incomplete attachments before sending: `attachments.filter(a => a.filename && (a.content || a.path))`.
- If loading content asynchronously, await the file read so the variable is not undefined: `content: fs.readFileSync(p)`.
Example fix
// before
attachments: [{ filename: 'invoice.pdf' }]
// after (local file)
attachments: [{ filename: 'invoice.pdf', path: '/tmp/invoice.pdf' }]
// after (remote URL)
attachments: [{ filename: 'invoice.pdf', path: 'https://cdn.example.com/invoice.pdf' }]
// after (in-memory)
attachments: [{ filename: 'invoice.pdf', content: Buffer.from(raw) }] Defensive patterns
Strategy: validation
Validate before calling
function isAttachmentComplete(a) {
return Boolean(a && a.filename && (a.content != null || a.path != null && a.path !== ''))
}
const safe = attachments.filter(isAttachmentComplete) Type guard
import type { SendEmailOptions } from 'payload'
function isCompleteAttachment(a: Partial<SendEmailOptions['attachments'][number]>): a is SendEmailOptions['attachments'][number] {
return Boolean(a.filename && (a.content != null || (!!a.path && a.path !== '')))
} Try / catch
try {
await payload.sendEmail({ to, from, subject, attachments: safe })
} catch (e) {
if (e instanceof APIError && e.message.includes('Attachment is missing both')) {
// surface a friendly 'attachment source missing' error to the caller
}
throw e
} Prevention
- Type attachment content as `string | Buffer` and path as `string | URL` at the call site.
- Filter attachments through `isAttachmentComplete` before sending.
- When loading files async, await the read so the content variable is never undefined.
When it happens
Trigger: Calling sendEmail with `attachments: [{ filename: 'report.pdf' }]` (no content/path), or passing an attachment built from a partial object where the file source was conditionally omitted, e.g. `attachments: [{ filename, path: maybePath }]` when `maybePath` is undefined. Also when `attachment.path` is set to an empty string or null while `attachment.content` is also absent.
Common situations: Building attachments dynamically from uploaded files where the upload stream/path variable is undefined; copy-pasting an attachment example that only shows filename; migrating from another provider whose API treated content as optional; passing a URL string directly as the attachment instead of wrapping it as `{ filename, path: url }`.
Related errors
- Attachment is missing filename
- Attachment content must be a string or a buffer
- Error sending email: ${statusCode}
- Username or email is required
- Email is required.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/4da17403f67e9448.
Report an issue: GitHub.