{"record":{"id":"2a66e43081c2ec3a","repo":"Budibase/budibase","slug":"smtp-password-is-required","errorCode":null,"errorMessage":"SMTP password is required","messagePattern":"SMTP password is required","errorType":"validation","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"packages/worker/src/api/controllers/global/configs.ts","lineNumber":180,"sourceCode":"\nasync function hasActivatedConfig(ssoConfigs?: SSOConfigs) {\n  if (!ssoConfigs) {\n    ssoConfigs = await getSSOConfigs()\n  }\n  return !!Object.values(ssoConfigs).find(c => c?.activated)\n}\n\nasync function processSMTPConfig(\n  config: SMTPInnerConfig,\n  existingConfig?: SMTPInnerConfig\n) {\n  if (config.auth?.pass === PASSWORD_REPLACEMENT) {\n    // if the password is being replaced, use the existing password\n    if (existingConfig && existingConfig.auth?.pass) {\n      config.auth.pass = existingConfig.auth.pass\n    } else {\n      // otherwise, throw an error\n      throw new BadRequestError(\"SMTP password is required\")\n    }\n  }\n  await email.verifyConfig(config)\n}\n\nasync function processSettingsConfig(\n  config: SettingsInnerConfig & SettingsBrandingConfig,\n  existingConfig?: SettingsInnerConfig & SettingsBrandingConfig\n) {\n  if (config.isSSOEnforced) {\n    const valid = await hasActivatedConfig()\n    if (!valid) {\n      throw new Error(\"Cannot enforce SSO without an activated configuration\")\n    }\n  }\n\n  // always preserve file attributes\n  // these should be set via upload instead","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/api/controllers/global/configs.ts#L162-L198","documentation":"processSMTPConfig supports a PASSWORD_REPLACEMENT placeholder that signals 'keep the existing stored password'. If the placeholder is sent but no existing config with a stored password exists, there is no password to retain, so a BadRequestError is thrown before email.verifyConfig runs.","triggerScenarios":"Saving an SMTP config via the configs API where config.auth.pass equals the PASSWORD_REPLACEMENT marker while existingConfig is missing or existingConfig.auth.pass is empty.","commonSituations":"UI edit forms that always submit the placeholder to avoid echoing secrets, but the SMTP config was never fully saved before, or an API client copies an update payload from another tenant where the password was never set.","solutions":["Send the actual SMTP password in auth.pass instead of the placeholder when no password is stored yet","Complete initial SMTP setup once with a real password; subsequent updates can use the placeholder","Verify the existing config was saved with auth.pass populated before sending PASSWORD_REPLACEMENT","Check the existingConfig lookup (tenant/type) is correct so the stored password is found"],"exampleFix":"// before\n{ \"auth\": { \"user\": \"smtp@example.com\", \"pass\": \"__REPLACEMENT__\" } }\n// after\n{ \"auth\": { \"user\": \"smtp@example.com\", \"pass\": \"<real-smtp-password>\" } }","handlingStrategy":"validation","validationCode":"const isReplacement = cfg.auth?.pass === PASSWORD_REPLACEMENT\nconst stored = existingConfig?.auth?.pass\nif (isReplacement && !stored) {\n  // prompt user for a real password before calling save\n}","typeGuard":"function hasSmtpPassword(c: { auth?: { pass?: string } } | undefined): c is { auth: { pass: string } } {\n  return typeof c?.auth?.pass === \"string\" && c.auth.pass.length > 0 && c.auth.pass !== PASSWORD_REPLACEMENT\n}","tryCatchPattern":"try {\n  await configApi.save(smtpConfig)\n} catch (err) {\n  if (err.status === 400 && err.message.includes(\"SMTP password is required\")) {\n    // re-open form and require the password field\n  } else { throw err }\n}","preventionTips":["On first-time SMTP setup always send the real password, never the placeholder","Only send PASSWORD_REPLACEMENT when updating a config known to have a stored password","Verify the existing config via GET before submitting an update","Keep the password field required in UIs when no stored password exists"],"tags":["smtp","email","configuration","bad-request"],"backgroundTag":"missing-required-secret","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}