SonarSource/sonarqube · critical · java.lang.IllegalStateException

Failed to read content of table SCHEMA_MIGRATIONS

Error message

Failed to read content of table SCHEMA_MIGRATIONS

What it means

MigrationHistoryImpl.getLastMigrationNumber reads the SCHEMA_MIGRATIONS table to find the last applied migration version. An SQLException while querying it is wrapped in IllegalStateException "Failed to read content of table SCHEMA_MIGRATIONS". This means the migration framework cannot determine which migrations have already been applied.

Source

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

    }
  }

  @Override
  public void stop() {
    // nothing to do
  }

  @Override
  public Optional<Long> getLastMigrationNumber() {
    try (Connection connection = database.getDataSource().getConnection()) {
      List<Long> versions = selectVersions(connection);

      if (!versions.isEmpty()) {
        return Optional.of(versions.get(versions.size() - 1));
      }
      return Optional.empty();
    } catch (SQLException e) {
      throw new IllegalStateException("Failed to read content of table " + SCHEMA_MIGRATIONS_TABLE, e);
    }
  }

  @Override
  public void done(RegisteredMigrationStep dbMigration) {
    long migrationNumber = dbMigration.getMigrationNumber();
    try (Connection connection = database.getDataSource().getConnection();
      PreparedStatement statement = connection.prepareStatement("insert into schema_migrations(version) values (?)")) {

      statement.setString(1, String.valueOf(migrationNumber));
      statement.execute();
      if (!connection.getAutoCommit()) {
        connection.commit();
      }
    } catch (SQLException e) {
      throw new IllegalStateException(String.format("Failed to insert row with value %s in table %s", migrationNumber, SCHEMA_MIGRATIONS_TABLE), e);
    }
  }

View on GitHub (pinned to 184c821202)

Solutions

  1. Inspect the underlying SQLException cause to identify the exact failure (table missing, permission, lock, connection).
  2. Grant the sonar.jdbc.user SELECT (and generally DDL/DML) rights on the schema, or run migrations as the schema owner.
  3. If the table was manually removed or corrupted, restore it from backup or, for a fresh install, recreate the empty schema and let migrations run from scratch.
  4. Check for database locks/failed transactions from a previous interrupted migration and resolve them before restarting.

Example fix

// before — insufficient privileges
GRANT CONNECT ON DATABASE sonar TO sonar_user;

// after — grant schema rights so SCHEMA_MIGRATIONS is readable/writable
GRANT ALL PRIVILEGES ON SCHEMA public TO sonar_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO sonar_user;
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-migration check
SELECT to_regclass('public.schema_migrations') IS NOT NULL AS table_exists;
SELECT has_table_privilege('sonar_user', 'schema_migrations', 'SELECT');

Try / catch

try {
  history.getLastMigrationNumber();
} catch (IllegalStateException e) {
  // inspect SQLException cause: grant rights, restore table, or rebuild schema
  LOGGER.error("SCHEMA_MIGRATIONS unreadable; cause={}", e.getCause());
}

Prevention

When it happens

Trigger: The SELECT against SCHEMA_MIGRATIONS (called during start/migration bootstrap) throws SQLException — table missing or locked, permission denied, or a connection/SQL failure.

Common situations: User lacks SELECT rights on the table; the table was dropped or the schema was partially wiped manually; DB connection dropped mid-migration; an aborted earlier migration left the database locked or in a bad state.

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 SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/b584256eeff5e791. Report an issue: GitHub.