YunaiV/yudao-cloud · critical · FlowableException

couldn't lookup datasource from

Error message

couldn't lookup datasource from {}: {}

What it means

Flowable's AbstractEngineConfiguration.initDataSource() tries InitialContext().lookup(dataSourceJndiName) when no DataSource bean was injected. Any NamingException/ClassCastException/communication failure from the JNDI provider is wrapped in this FlowableException, including the JNDI name in the message.

Solutions

  1. Verify the exact JNDI name against the server's JNDI tree (e.g. WildFly admin console, Tomcat's ResourceLink + web.xml resource-ref).
  2. On Spring Boot / standalone, skip JNDI and configure jdbcUrl/jdbcDriver/jdbcUsername directly, or inject a DataSource bean via setDataSource().
  3. Ensure the lookup target is really a javax.sql.DataSource and the app has permission to read it.
  4. For embedded Tomcat, register the resource in server.context.xml (or TomcatServletWebServerFactory customization) and the ResourceLink in context.xml.

Example fix

// before
cfg.setDataSourceJndiName("java:comp/env/jdbc/flowable"); // fails in Spring Boot

// after
cfg.setJdbcUrl("jdbc:dm://localhost:5236");
cfg.setJdbcDriver("dm.jdbc.driver.DmDriver");
cfg.setJdbcUsername("SYSDBA");
cfg.setJdbcPassword("***");
Defensive patterns

Strategy: validation

Validate before calling

// Fail fast before engine build with a clear message
if (cfg.getDataSource() == null && cfg.getDataSourceJndiName() != null) {
    try {
        DataSource ds = (DataSource) new InitialContext().lookup(cfg.getDataSourceJndiName());
        Assert.notNull(ds, "JNDI name resolved to null");
    } catch (Exception e) {
        throw new IllegalStateException("JNDI datasource unavailable: " + cfg.getDataSourceJndiName(), e);
    }
}

Try / catch

try {
    processEngine = cfg.buildProcessEngine();
} catch (FlowableException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("couldn't lookup datasource")) {
        // fall back to direct JDBC config or fail with actionable guidance
        throw new IllegalStateException("Configure a valid JNDI name or set jdbc* properties", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: ProcessEngineConfiguration is built with setDataSourceJndiName("java:comp/env/jdbc/ds") but the app runs outside a container with no JNDI tree, or the name is misspelled, or the bound object is not a javax.sql.DataSource (cast fails), or the app server resource is not deployed/started.

Common situations: Moving a Flowable app from a full app server (WildFly/TomEE with JNDI) to Spring Boot embedded Tomcat which has no java:comp/env by default; typos in the JNDI name; resource adapter not yet published during early startup; running unit tests without a JNDI implementation.

Related errors


AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14). Data as JSON: /api/errors/57310b6c6875c3f2. Report an issue: GitHub.

Appendix: source

Thrown at sql/dm/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java:446

    /**
     * Define a max length for storing String variable types in the database. Mainly used for the Oracle NVARCHAR2 limit of 2000 characters
     */
    protected int maxLengthStringVariableType = -1;

    protected void initEngineConfigurations() {
        addEngineConfiguration(getEngineCfgKey(), getEngineScopeType(), this);
    }

    // DataSource
    // ///////////////////////////////////////////////////////////////

    protected void initDataSource() {
        if (dataSource == null) {
            if (dataSourceJndiName != null) {
                try {
                    dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName);
                } catch (Exception e) {
                    throw new FlowableException("couldn't lookup datasource from " + dataSourceJndiName + ": " + e.getMessage(), e);
                }

            } else if (jdbcUrl != null) {
                if ((jdbcDriver == null) || (jdbcUsername == null)) {
                    throw new FlowableException("DataSource or JDBC properties have to be specified in a process engine configuration");
                }

                logger.debug("initializing datasource to db: {}", jdbcUrl);

                if (logger.isInfoEnabled()) {
                    logger.info("Configuring Datasource with following properties (omitted password for security)");
                    logger.info("datasource driver : {}", jdbcDriver);
                    logger.info("datasource url : {}", jdbcUrl);
                    logger.info("datasource user name : {}", jdbcUsername);
                }

                PooledDataSource pooledDataSource = new PooledDataSource(this.getClass().getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword);

View on GitHub (pinned to 477be9dd49)