hibernate/hibernate-orm · error · SchemaManagementException

Missing index named `%s` on table `%s`

Error message

Missing index named `%s` on table `%s`

What it means

Index validation in the schema validator: the mapping declares an index that tableInformation.getIndex(name) cannot find in JDBC metadata. Validation only runs when hibernate.tooling.schema.index_validation is set to NAMED (skips generated names starting with 'IDX') or ALL, so this fires for explicitly named mapped indexes (e.g. @Table(indexes = @Index(name = ...))) that do not exist in the database under that exact identifier.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java:204

			assert StringHelper.isNotEmpty( rawName );
			assert Objects.equals( rawName, index.getName() );
			if ( validationType == ConstraintValidationType.NONE ) {
				return;
			}
			else if ( validationType == ConstraintValidationType.NAMED ) {
				if ( rawName.startsWith( "IDX" ) ) {
					// this is not a great check as the user could very well
					// have explicitly chosen a name that starts with this as well,
					// but...
					return;
				}
			}

			var name = metadata.getDatabase().toIdentifier( rawName );
			final IndexInformation indexInformation = tableInformation.getIndex( name );

			if ( indexInformation == null ) {
				throw new SchemaManagementException(
						String.format(
								ROOT,
								"Missing index named `%s` on table `%s`",
								name.render( dialect ),
								tableInformation.getName().render()
						)
				);
			}

			var indicesMatch = true;
			assert index.getSelectables().size() == index.getColumnSpan();
			if ( index.getColumnSpan() != indexInformation.getIndexedColumns().size() ) {
				indicesMatch = false;
			}
			else {
				for ( int i = 0; i < index.getSelectables().size(); i++ ) {
					final Selectable column = index.getSelectables().get( i );
					final ColumnInformation columnInfo = indexInformation.getIndexedColumns().get( i );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Create the index in the database with exactly the declared name: CREATE INDEX <name> ON <table> (<columns>).
  2. Or rename the @Index declaration to the name that already exists in the database.
  3. If indexes are DBA/migration-managed, remove the declaration from the mapping or set hibernate.tooling.schema.index_validation=NONE.
  4. Keep index DDL in the same migration pipeline as table DDL so mappings and schema never diverge.

Example fix

// before: index declared in the mapping but never created in the database
@Table(name = "orders", indexes = @Index(name = "orders_customer_idx", columnList = "customer_id"))

// after: ship the matching migration, mapping unchanged
// CREATE INDEX orders_customer_idx ON orders (customer_id);
@Table(name = "orders", indexes = @Index(name = "orders_customer_idx", columnList = "customer_id"))
Defensive patterns

Strategy: validation

Validate before calling

// Before validate with index_validation=NAMED/ALL, confirm declared indexes exist by name
try (Connection c = dataSource.getConnection();
     ResultSet rs = c.getMetaData().getIndexInfo(null, null, "orders", false, false)) {
    Set<String> actual = new HashSet<>();
    while (rs.next()) {
        String n = rs.getString("INDEX_NAME");
        if (n != null) actual.add(n.toLowerCase());
    }
    for (String declared : List.of("orders_customer_idx")) {
        if (!actual.contains(declared.toLowerCase())) {
            throw new IllegalStateException("Index " + declared + " missing - run its migration before validate");
        }
    }
}

Try / catch

try {
    new SchemaValidator().validate(metadata, serviceRegistry);
} catch (SchemaManagementException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Missing index named")) {
        // create the index with the declared name or align the @Index name, then retry
    }
    throw e;
}

Prevention

When it happens

Trigger: hibernate.hbm2ddl.auto=validate combined with hibernate.tooling.schema.index_validation=NAMED or ALL, where the database lacks an index with the declared name: the index declaration was added without shipping a migration; the migration created the index under a different name; identifier case/quoting differs from what the driver reports (PostgreSQL folding).

Common situations: Declarative @Index added to an entity mid-project and validated against legacy schemas; migration tools auto-generating index names different from the mapped ones; enabling index validation for the first time on an existing database; Postgres lowercasing unquoted mixed-case index names.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/1784092242c969ed. Report an issue: GitHub.