usememos/memos · error

enabled notification email requires smtpHost, a positive smt

Error message

enabled notification email requires smtpHost, a positive smtpPort, and fromEmail

What it means

Thrown when a NOTIFICATION instance-setting file enables email (notification.email.enabled == true) but the SMTP details are incomplete: smtpHost is blank/whitespace, smtpPort is <= 0, or fromEmail is blank. An enabled notifier without a reachable SMTP endpoint would fail every notification at runtime, so deployment validation fails fast at startup.

Source

Thrown at store/deployment_config.go:277

				{name: "bucket", value: s3Config.Bucket},
			} {
				if strings.TrimSpace(field.value) == "" {
					return errors.Errorf("storageSetting default S3 config.%s is required", field.name)
				}
			}
		}
	case storepb.InstanceSettingKey_MEMO_RELATED:
		if setting.GetMemoRelatedSetting() == nil {
			return errors.New("memoRelatedSetting must be populated for key MEMO_RELATED")
		}
	case storepb.InstanceSettingKey_NOTIFICATION:
		notification := setting.GetNotificationSetting()
		if notification == nil {
			return errors.New("notificationSetting must be populated for key NOTIFICATION")
		}
		if email := notification.Email; email != nil && email.Enabled {
			if strings.TrimSpace(email.SmtpHost) == "" || email.SmtpPort <= 0 || strings.TrimSpace(email.FromEmail) == "" {
				return errors.New("enabled notification email requires smtpHost, a positive smtpPort, and fromEmail")
			}
			if email.UseTls && email.UseSsl {
				return errors.New("notification email cannot enable both useTls and useSsl")
			}
		}
	case storepb.InstanceSettingKey_AI:
		if setting.GetAiSetting() == nil {
			return errors.New("aiSetting must be populated for key AI")
		}
		if err := normalizeDeploymentAISetting(setting.GetAiSetting()); err != nil {
			return err
		}
	case storepb.InstanceSettingKey_BASIC, storepb.InstanceSettingKey_TAGS:
		return errors.Errorf("key %s cannot be deployment configured", setting.Key)
	default:
		return errors.Errorf("unsupported instance setting key %s", setting.Key)
	}
	return nil

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Fill in all three fields: "smtpHost": "smtp.example.com", "smtpPort": 587, "fromEmail": "memos@example.com".
  2. If SMTP is not actually configured yet, set "enabled": false until credentials exist.
  3. Check rendered secrets for whitespace-only values if the file is generated.

Example fix

// before
{ "key": "NOTIFICATION", "notificationSetting": { "email": { "enabled": true, "smtpHost": "", "smtpPort": 0, "fromEmail": "" } } }

// after
{ "key": "NOTIFICATION", "notificationSetting": { "email": { "enabled": true, "smtpHost": "smtp.example.com", "smtpPort": 587, "fromEmail": "memos@example.com" } } }
Defensive patterns

Strategy: validation

Validate before calling

if e := notification.GetEmail(); e != nil && e.GetEnabled() {
    if strings.TrimSpace(e.GetSmtpHost()) == "" || e.GetSmtpPort() <= 0 || strings.TrimSpace(e.GetFromEmail()) == "" {
        return errors.New("enabled email requires smtpHost, positive smtpPort, fromEmail")
    }
}

Type guard

func enabledEmailIsComplete(e *storepb.EmailSetting) bool {
    return !e.GetEnabled() ||
        (strings.TrimSpace(e.GetSmtpHost()) != "" && e.GetSmtpPort() > 0 && strings.TrimSpace(e.GetFromEmail()) != "")
}

Prevention

When it happens

Trigger: "email": { "enabled": true } with smtpHost/fromEmail missing or empty, or smtpPort set to 0 / a negative number; secret-mounting that renders empty strings for host or from-email.

Common situations: Enabling email before provisioning SMTP credentials; environment templating (envsubst) substituting empty variables for SMTP_HOST or FROM_EMAIL; forgetting the port when using a mail relay on a non-standard port.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/262bdd0920c1e98e. Report an issue: GitHub.