{"record":{"id":"07bc04969b70e2f8","repo":"Budibase/budibase","slug":"attachments-must-have-both-url-and-filename-ke","errorCode":null,"errorMessage":"Attachments must have both \"url\" and \"filename\" keys. You have provided: ${providedKeys}","messagePattern":"Attachments must have both \"url\" and \"filename\" keys\\. You have provided: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/automations/automationUtils.ts","lineNumber":158,"sourceCode":"  }\n}\n\nfunction normalizeSingleAttachment(\n  input: string | AutomationAttachment\n): AutomationAttachment | null {\n  if (typeof input === \"string\") {\n    try {\n      const parsed = JSON.parse(input)\n      return normalizeSingleAttachment(parsed)\n    } catch {\n      return { url: input, filename: deriveFilenameFromUrl(input) }\n    }\n  }\n\n  const url: string | undefined = input.url\n  if (!url) {\n    const providedKeys = Object.keys(input).join(\", \")\n    throw new Error(\n      `Attachments must have both \"url\" and \"filename\" keys. You have provided: ${providedKeys}`\n    )\n  }\n  const filename: string =\n    input.filename ?? input.name ?? deriveFilenameFromUrl(url)\n\n  return { url, filename }\n}\n\nfunction normalizeAttachmentValue(\n  value: string | AutomationAttachment | AutomationAttachment[]\n): AutomationAttachment | AutomationAttachment[] | null {\n  if (value == null) return null\n\n  if (typeof value === \"string\") {\n    try {\n      const parsed = JSON.parse(value)\n      return normalizeAttachmentValue(parsed)","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/automations/automationUtils.ts#L140-L176","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nattachments: [{ filename: \"report.pdf\" }]\n// after\nattachments: [{ url: \"https://example.com/report.pdf\", filename: \"report.pdf\" }]","handlingStrategy":"validation","validationCode":"const input = maybeAttachment\nif (typeof input !== \"object\" || input === null || !(\"url\" in input) || !(input as any).url) {\n  throw new Error(`Attachment requires a url; got keys: ${Object.keys(input ?? {}).join(\", \")}`)\n}","typeGuard":"const isAttachment = (v: unknown): v is { url: string; filename?: string } =>\n  typeof v === \"object\" && v !== null && \"url\" in v && typeof (v as { url: unknown }).url === \"string\" && (v as { url: string }).url !== \"\"","tryCatchPattern":"try {\n  await runAutomation(inputs)\n} catch (e) {\n  if (/Attachments must have both/.test(e.message)) {\n    // inspect the binding that supplies the attachment url\n  } else throw e\n}","preventionTips":["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."],"tags":["automation","attachments","validation","input-shape"],"backgroundTag":"missing-required-field","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}