Budibase/budibase · error · BadRequestError
SMTP password is required
Error message
SMTP password is required
What it means
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.
Source
Thrown at packages/worker/src/api/controllers/global/configs.ts:180
async function hasActivatedConfig(ssoConfigs?: SSOConfigs) {
if (!ssoConfigs) {
ssoConfigs = await getSSOConfigs()
}
return !!Object.values(ssoConfigs).find(c => c?.activated)
}
async function processSMTPConfig(
config: SMTPInnerConfig,
existingConfig?: SMTPInnerConfig
) {
if (config.auth?.pass === PASSWORD_REPLACEMENT) {
// if the password is being replaced, use the existing password
if (existingConfig && existingConfig.auth?.pass) {
config.auth.pass = existingConfig.auth.pass
} else {
// otherwise, throw an error
throw new BadRequestError("SMTP password is required")
}
}
await email.verifyConfig(config)
}
async function processSettingsConfig(
config: SettingsInnerConfig & SettingsBrandingConfig,
existingConfig?: SettingsInnerConfig & SettingsBrandingConfig
) {
if (config.isSSOEnforced) {
const valid = await hasActivatedConfig()
if (!valid) {
throw new Error("Cannot enforce SSO without an activated configuration")
}
}
// always preserve file attributes
// these should be set via upload insteadView on GitHub (pinned to a81a902e9a)
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
Example fix
// before
{ "auth": { "user": "smtp@example.com", "pass": "__REPLACEMENT__" } }
// after
{ "auth": { "user": "smtp@example.com", "pass": "<real-smtp-password>" } } Defensive patterns
Strategy: validation
Validate before calling
const isReplacement = cfg.auth?.pass === PASSWORD_REPLACEMENT
const stored = existingConfig?.auth?.pass
if (isReplacement && !stored) {
// prompt user for a real password before calling save
} Type guard
function hasSmtpPassword(c: { auth?: { pass?: string } } | undefined): c is { auth: { pass: string } } {
return typeof c?.auth?.pass === "string" && c.auth.pass.length > 0 && c.auth.pass !== PASSWORD_REPLACEMENT
} Try / catch
try {
await configApi.save(smtpConfig)
} catch (err) {
if (err.status === 400 && err.message.includes("SMTP password is required")) {
// re-open form and require the password field
} else { throw err }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Please contact your platform administrator, SMTP is not conf
- Email trigger inputs are required
- No secret key provided
- Workspace DB not found - self-host users using cloud don't h
- CouchDB username not set
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/2a66e43081c2ec3a.
Report an issue: GitHub.