SonarSource/sonarqube · critical · java.lang.IllegalStateException

Failed to create table schema_migrations

Error message

Failed to create table schema_migrations

What it means

Thrown by MigrationHistoryTableImpl.start() when creating the sonar-core-schema_migrations table fails with a SQLException. This table tracks which DB migrations have been applied; without it SonarQube cannot boot its database schema layer. The IllegalStateException wraps the underlying SQLException as the cause.

Source

Thrown at server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImpl.java:48

import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;

public class MigrationHistoryTableImpl implements MigrationHistoryTable {
  private static final String VERSION_COLUMN_NAME = "version";

  private final Database database;

  public MigrationHistoryTableImpl(Database database) {
    this.database = database;
  }

  @Override
  public void start() {
    try (Connection connection = createDdlConnection(database)) {
      if (!DatabaseUtils.tableExists(NAME, connection)) {
        createTable(connection);
      }
    } catch (SQLException e) {
      throw new IllegalStateException("Failed to create table " + NAME, e);
    }
  }

  private void createTable(Connection connection) throws SQLException {
    List<String> sqls = new CreateTableBuilder(database.getDialect(), NAME)
      .addColumn(VarcharColumnDef.newVarcharColumnDefBuilder().setColumnName(VERSION_COLUMN_NAME).setIsNullable(false).setLimit(255).build())
      .build();

    LoggerFactory.getLogger(MigrationHistoryTableImpl.class).info("Creating table " + NAME);
    for (String sql : sqls) {
      execute(connection, sql);
    }
  }

  private static Connection createDdlConnection(Database database) throws SQLException {
    Connection res = database.getDataSource().getConnection();
    res.setAutoCommit(false);
    return res;

View on GitHub (pinned to 184c821202)

Solutions

  1. Check the root SQLException cause in logs for the actual JDBC error (access denied, unknown database, connection refused).
  2. Verify sonar.jdbc.url/username/password point to an existing, reachable database.
  3. Grant the database user CREATE TABLE privileges on the target schema.
  4. Test connectivity to the DB host/port and retry startup.

Example fix

// before
sonar.jdbc.url=jdbc:postgresql://localhost:5432/wrongdb
// after
sonar.jdbc.url=jdbc:postgresql://localhost:5432/sonar
Defensive patterns

Strategy: try-catch

Validate before calling

try (Connection c = DriverManager.getConnection(url, user, pass)) { DatabaseMetaData md = c.getMetaData(); } catch (SQLException e) { /* fail fast before migration */ }

Try / catch

try { historyTable.start(); } catch (IllegalStateException e) { logger.error("Migration history table creation failed; check DB connectivity/privileges", e.getCause()); throw e; }

Prevention

When it happens

Trigger: Calling start() when the database is unreachable, credentials are wrong, the configured user lacks CREATE TABLE privileges, or the schema/catalog does not exist.

Common situations: First startup against a fresh database with wrong JDBC settings; a DBA restricted the sonar user's DDL rights; network/firewall drops the JDBC connection; unsupported DB version or dialect.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/8205406562bb2e55. Report an issue: GitHub.