SonarSource/sonarqube · error · java.lang.IllegalStateException

Fail to execute

Error message

Fail to execute %s

What it means

The non-retry path of DdlChange.execute: a plain Exception/SQLException from executing DDL on the first attempt, with no auto-correction applied, is wrapped in IllegalStateException with message "Fail to execute <sql>". The exception preserves the failing SQL as the message so developers can see exactly which statement broke.

Solutions

  1. Look at the wrapped cause for the vendor error code (ORA-*, SQL Server error, etc.) to diagnose the root issue.
  2. Verify schema state matches the expected pre-migration state (no partial/duplicate objects).
  3. Ensure the DB user has DDL rights; drop/rename conflicting objects if a stale one blocks the migration.
Defensive patterns

Strategy: try-catch

Validate before calling

// Check object existence before executing DDL
// H2/PG: SELECT 1 FROM information_schema.tables WHERE table_name = ?
// Oracle: SELECT 1 FROM user_tables WHERE table_name = ?

Try / catch

try {
  ddlChange.execute(context);
} catch (IllegalStateException e) {
  if (e.getCause() instanceof SQLException se && se.getErrorCode() == ORA_NAME_ALREADY_USED) {
    LOG.warn("Object already exists, continuing migration");
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Any DDL statement from a migration step (CreateTableBuilder, CreateIndexBuilder, etc. via SqlStatement.execute) fails on first execution — e.g. table already exists, duplicate index, insufficient privileges.

Common situations: Re-running migrations against a dirty schema; migrating a database restored from a different SonarQube version; database permissions/locks preventing DDL.

Related errors


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

Appendix: source

Thrown at server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DdlChange.java:108

      try (Statement stmt = writeConnection.createStatement()) {
        stmt.execute(sql);
        writeConnection.commit();
      } catch (SQLException e) {
        if (errorCount < ERROR_HANDLING_THRESHOLD) {
          String message = e.getMessage();
          if (message.contains("ORA-01451")) {
            String newSql = nullPattern.matcher(sql).replaceFirst("");
            execute(original, newSql, errorCount + 1);
            return;
          } else if (message.contains("ORA-01442")) {
            String newSql = notNullPattern.matcher(sql).replaceFirst("");
            execute(original, newSql, errorCount + 1);
            return;
          }
        }
        throw new IllegalStateException(messageForIseOf(original, sql, errorCount), e);
      } catch (Exception e) {
        throw new IllegalStateException(messageForIseOf(original, sql, errorCount), e);
      }
    }

    private static String messageForIseOf(String original, String sql, int errorCount) {
      if (!original.equals(sql) || errorCount > 0) {
        return format("Fail to execute %s %n (caught %s error, original was %s)", sql, errorCount, original);
      } else {
        return format("Fail to execute %s", sql);
      }
    }

    @Override
    public void execute(String... sqls) {
      execute(asList(sqls));
    }

    @Override
    public void execute(List<String> sqls) {

View on GitHub (pinned to 184c821202)