{"record":{"id":"151565490b1df049","repo":"Budibase/budibase","slug":"automation-id-is-required-to-test-a-saved-password","errorCode":null,"errorMessage":"Automation ID is required to test a saved password","messagePattern":"Automation ID is required to test a saved password","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/api/controllers/automation.ts","lineNumber":234,"sourceCode":"    action: await actionDefs(),\n  }\n}\n\nasync function hydrateEmailConnectionPassword(\n  inputs: TestEmailConnectionRequest\n): Promise<EmailTriggerInputs> {\n  const { automationId, ...emailInputs } = inputs\n  if (emailInputs.authType === EmailTriggerAuthType.OAUTH2) {\n    delete emailInputs.password\n    return emailInputs\n  }\n\n  if (!isMaskedPassword(emailInputs.password)) {\n    return emailInputs\n  }\n\n  if (!automationId) {\n    throw new HTTPError(\n      \"Automation ID is required to test a saved password\",\n      400\n    )\n  }\n\n  const automation = await context\n    .getWorkspaceDB()\n    .tryGet<Automation>(automationId)\n  if (!automation) {\n    throw new HTTPError(\"Automation not found\", 404)\n  }\n\n  const trigger = automation.definition.trigger\n  if (!trigger) {\n    throw new HTTPError(\"No trigger found for automation\", 400)\n  }\n  if (!isEmailTrigger(trigger)) {\n    throw new HTTPError(\"Automation trigger is not an email trigger\", 400)","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/automation.ts#L216-L252","documentation":"hydrateEmailConnectionPassword resolves a masked saved IMAP password for testing an email (IMAP) trigger. Because the stored password lives on a saved automation's trigger inputs, a masked password cannot be resolved without knowing which automation to read from, so the request is rejected with HTTP 400.","triggerScenarios":"Calling testEmailConnection with emailInputs.password still in masked form (e.g. '####') while the request body omits automationId.","commonSituations":"Testing an email connection from a generic connection-test UI that isn't tied to a saved automation; copying inputs from a masked GET response without passing the automation ID back.","solutions":["Include automationId in the test request body","Send the real plaintext password instead of the masked value if testing outside an automation context","Fetch the automation first so its ID is available when testing"],"exampleFix":"// before\nawait api.post('/api/automations/email/test', { emailInputs })\n// after\nawait api.post('/api/automations/email/test', { emailInputs, automationId: 'auto_123' })","handlingStrategy":"validation","validationCode":"const isMasked = (pw?: string) => !pw || /^[#•*]+$/.test(pw)\nif (isMasked(emailInputs.password) && !automationId) {\n  throw new Error('automationId is required when using a saved (masked) password')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await testEmailConnection({ emailInputs, automationId })\n} catch (e) {\n  if (e.status === 400 && e.message.includes('Automation ID is required')) {\n    // prompt user to provide the automation or the real password\n  }\n}","preventionTips":["Always pass automationId when testing connections tied to saved automations","Track whether a password field is masked before submitting test requests","Send the plaintext password when testing outside an automation context"],"tags":["automation","imap","validation","missing-parameter"],"backgroundTag":"missing-required-parameter","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}