Activiti/Activiti · error · ActivitiException

couldn't db schema

Error message

couldn't ${operation} db schema: ${exceptionSqlStatement}

What it means

Generic wrapper for failures while executing a schema script (create/drop/upgrade/check) in executeSchemaResource: the SQL statement recorded in exceptionSqlStatement failed, so the whole '<operation> db schema' is aborted with an ActivitiException carrying the failing statement. It signals the database rejected a DDL/DML statement during schema management.

Solutions

  1. Read the failing statement in the message and the underlying SQLException cause to see the DB error.
  2. If tables already exist / partial state, either drop the old ACT_* tables or set databaseSchemaUpdate=false and manage schema manually.
  3. Grant the DB user DDL privileges (CREATE, ALTER, DROP) needed by the schema scripts.
  4. Confirm databaseType detection is correct (jdbc url/driver) so the right dialect scripts run.
  5. Run schema scripts manually with your DB tool to get better diagnostics, then start the engine with schema update disabled.

Example fix

// before
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// after (create schema manually via SQL scripts, then)
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
Defensive patterns

Strategy: try-catch

Try / catch

try {
  processEngine = cfg.buildProcessEngine();
} catch (ActivitiException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("couldn't ") && e.getMessage().contains("db schema:")) {
    log.error("Schema op failed on statement: {} | cause: {}", e.getMessage(), e.getCause(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Engine startup with databaseSchemaUpdate=true (or explicit schema create/drop) executes the SQL statements of a schema resource; one statement throws a SQLException, caught by executeSchemaResource's catch block, and 'couldn't <operation> db schema: <sql>' is thrown.

Common situations: Tables already exist (partial prior create) causing 'table already exists' on create; insufficient DB user privileges for CREATE TABLE/ALTER; DB type mismatched so dialect-specific SQL fails; reserved keyword or type incompatibilities on unusual DB versions.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


AI-assisted analysis of Activiti/Activiti@56435b1a97 (2026-09-09). Data as JSON: /api/errors/871ecb8add1ab27a. Report an issue: GitHub.

Appendix: source

Thrown at activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/db/DbSqlSession.java:1286

                            log.error("problem during schema {}, statement {}", operation, sqlStatement, e);
                        } finally {
                            sqlStatement = null;
                        }
                    } else {
                        sqlStatement = addSqlStatementPiece(sqlStatement, line);
                    }
                }

                line = readNextTrimmedLine(reader);
            }

            if (exception != null) {
                throw exception;
            }

            log.debug("activiti db schema {} for component {} successful", operation, component);
        } catch (Exception e) {
            throw new ActivitiException("couldn't " + operation + " db schema: " + exceptionSqlStatement, e);
        }
    }

    /**
     * MySQL is funny when it comes to timestamps and dates.
     * <p>
     * More specifically, for a DDL statement like 'MYCOLUMN timestamp(3)': - MySQL 5.6.4+ has support for timestamps/dates with millisecond (or smaller) precision. The DDL above works and the data in
     * the table will have millisecond precision - MySQL < 5.5.3 allows the DDL statement, but ignores it. The DDL above works but the data won't have millisecond precision - MySQL 5.5.3 < [version] <
     * 5.6.4 gives and exception when using the DDL above.
     * <p>
     * Also, the 5.5 and 5.6 branches of MySQL are both actively developed and patched.
     * <p>
     * Hence, when doing auto-upgrade/creation of the Activiti tables, the default MySQL DDL file is used and all timestamps/datetimes are converted to not use the millisecond precision by string
     * replacement done in the method below.
     * <p>
     * If using the DDL files directly (which is a sane choice in production env.), there is a distinction between MySQL version < 5.6.
     */
    protected String updateDdlForMySqlVersionLowerThan56(String ddlStatements) {

View on GitHub (pinned to 56435b1a97)