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
- Check the root SQLException cause in logs for the actual JDBC error (access denied, unknown database, connection refused).
- Verify sonar.jdbc.url/username/password point to an existing, reachable database.
- Grant the database user CREATE TABLE privileges on the target schema.
- 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
- Validate JDBC URL/credentials with a smoke connection before starting migrations
- Grant DDL (CREATE TABLE) rights to the migration user
- Keep the DB reachable and monitor network/firewall rules
- Check the root cause (e.getCause()) rather than the wrapper message
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
- Failed to read content of table SCHEMA_MIGRATIONS
- Failed to insert row with value %s in table %s
- Error during processing of row: [%s]
- Fail to connect to database
- Unknown dialect '%s'
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/8205406562bb2e55.
Report an issue: GitHub.