flowable/flowable-engine · error · FlowableException

no SMTP host is configured for the mail server for tenant

Error message

no SMTP host is configured for the mail server for tenant ${tenantId}

What it means

When initializing per-tenant mail clients, Flowable iterates the configured tenant mail servers and builds a client for each. If a tenant's MailServerInfo has a null host (and no JNDI session applies), the engine cannot create an SMTP client for that tenant and throws this FlowableException naming the tenantId.

Solutions

  1. Set the host for the offending tenant: config.addMailServer(tenantId, new MailServerInfo(... host="smtp.tenant.com" ...)) or setMailServerHost equivalent per tenant
  2. Configure a JNDI mail session for the tenant via mailSessionsJndi so host lookup is bypassed
  3. Audit the tenant mail server map before building the engine and log/fail fast on entries missing a host
  4. Fall back to the default mail server by removing the incomplete tenant entry if the tenant should use default SMTP settings

Example fix

// before
MailServerInfo info = new MailServerInfo();
info.setMailServerUser("user");
config.addMailServer("tenantA", info);
// after
MailServerInfo info = new MailServerInfo();
info.setMailServerHost("smtp.tenant-a.com");
info.setMailServerUser("user");
config.addMailServer("tenantA", info);
Defensive patterns

Strategy: validation

Validate before calling

for (Map.Entry<String, MailServerInfo> e : config.getMailServers().entrySet()) {
    MailServerInfo info = e.getValue();
    if (info == null || info.getMailServerHost() == null || info.getMailServerHost().isEmpty()) {
        throw new IllegalStateException("Mail server host missing for tenant " + e.getKey());
    }
}

Prevention

When it happens

Trigger: Calling buildProcessEngine() with mailServers populated (via setMailServers/addMailServer) where the MailServerInfo for a specific tenantId has mailServerHost == null and no mail session JNDI is configured for that tenant.

Common situations: Multi-tenant deployments where one tenant's entry was added with only user/password/port but no host; a tenant properties map missing the host key; programmatic config that copies defaults but leaves host null.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/e34962ad71e3f342. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/ProcessEngineConfigurationImpl.java:1487

        }

        Collection<String> tenantIds = new HashSet<>(mailSessionsJndi.keySet());
        tenantIds.addAll(mailServers.keySet());

        if (!tenantIds.isEmpty()) {
            MailServerInfo defaultMailServer = getDefaultMailServer();
            for (String tenantId : tenantIds) {
                if (mailClients.containsKey(tenantId)) {
                    continue;
                }
                String sessionJndi = mailSessionsJndi.get(tenantId);
                MailServerInfo tenantMailServer = mailServers.get(tenantId);
                if (sessionJndi != null) {
                    mailClients.put(tenantId, FlowableMailClientCreator.createSessionClient(sessionJndi, tenantMailServer, defaultMailServer));
                } else if (tenantMailServer != null) {
                    String host = tenantMailServer.getMailServerHost();
                    if (host == null) {
                        throw new FlowableException("no SMTP host is configured for the mail server for tenant " + tenantId);
                    }
                    mailClients.put(tenantId, FlowableMailClientCreator.createHostClient(host, tenantMailServer, defaultMailServer));
                }
            }
        }
    }

    protected void initBusinessCalendarManager() {
        if (businessCalendarManager == null) {
            MapBusinessCalendarManager mapBusinessCalendarManager = new MapBusinessCalendarManager();
            mapBusinessCalendarManager.addBusinessCalendar(DurationBusinessCalendar.NAME, new DurationBusinessCalendar(this.clock));
            mapBusinessCalendarManager.addBusinessCalendar(DueDateBusinessCalendar.NAME, new DueDateBusinessCalendar(this.clock));
            mapBusinessCalendarManager.addBusinessCalendar(CycleBusinessCalendar.NAME, new CycleBusinessCalendar(this.clock));

            businessCalendarManager = mapBusinessCalendarManager;
        }
    }

View on GitHub (pinned to d6d39ce1c6)