Budibase/budibase · error · Error
Attachments must have both "url" and "filename" keys. You ha
Error message
Attachments must have both "url" and "filename" keys. You have provided: ${providedKeys} What it means
normalizeSingleAttachment coerces automation attachment inputs into {url, filename} objects. If the input object has no url key (and no resolvable URL), an Error is thrown listing the keys that were actually provided, since both url and filename are required for attachment handling.
Source
Thrown at packages/server/src/automations/automationUtils.ts:158
}
}
function normalizeSingleAttachment(
input: string | AutomationAttachment
): AutomationAttachment | null {
if (typeof input === "string") {
try {
const parsed = JSON.parse(input)
return normalizeSingleAttachment(parsed)
} catch {
return { url: input, filename: deriveFilenameFromUrl(input) }
}
}
const url: string | undefined = input.url
if (!url) {
const providedKeys = Object.keys(input).join(", ")
throw new Error(
`Attachments must have both "url" and "filename" keys. You have provided: ${providedKeys}`
)
}
const filename: string =
input.filename ?? input.name ?? deriveFilenameFromUrl(url)
return { url, filename }
}
function normalizeAttachmentValue(
value: string | AutomationAttachment | AutomationAttachment[]
): AutomationAttachment | AutomationAttachment[] | null {
if (value == null) return null
if (typeof value === "string") {
try {
const parsed = JSON.parse(value)
return normalizeAttachmentValue(parsed)View on GitHub (pinned to a81a902e9a)
Solutions
- Ensure the attachment input object includes a url key pointing to an accessible file URL.
- Check the upstream step/binding that should supply the URL — fix the binding so it resolves at runtime.
- Provide the full {url, filename} shape from the previous step's output.
- If filenames differ, pass {url, filename} explicitly rather than relying on name/derivation.
Example fix
// before
attachments: [{ filename: "report.pdf" }]
// after
attachments: [{ url: "https://example.com/report.pdf", filename: "report.pdf" }] Defensive patterns
Strategy: validation
Validate before calling
const input = maybeAttachment
if (typeof input !== "object" || input === null || !("url" in input) || !(input as any).url) {
throw new Error(`Attachment requires a url; got keys: ${Object.keys(input ?? {}).join(", ")}`)
} Type guard
const isAttachment = (v: unknown): v is { url: string; filename?: string } =>
typeof v === "object" && v !== null && "url" in v && typeof (v as { url: unknown }).url === "string" && (v as { url: string }).url !== "" Try / catch
try {
await runAutomation(inputs)
} catch (e) {
if (/Attachments must have both/.test(e.message)) {
// inspect the binding that supplies the attachment url
} else throw e
} Prevention
- Always include url in attachment objects; add filename explicitly when possible.
- Test bindings that supply attachment URLs with sample data.
- Validate upstream step outputs include url before feeding attachment fields.
- Use storage upload steps that return both url and filename.
When it happens
Trigger: An automation step (e.g. send email or webhook) receives an attachment object missing url — e.g. {filename: "a.txt"}, an empty object, or a mapped field whose URL binding resolved to null/undefined.
Common situations: Bindings that resolve to empty values at runtime; users pasting attachment objects from other systems with different key names; storage upload step output shape changed so only filename is produced.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Unexpected response ${response.statusText}
- No response received for attachment
- Unexpected response body stream type
- Invalid signed URL
- Automation ID is required to test a saved password
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/07bc04969b70e2f8.
Report an issue: GitHub.