hibernate/hibernate-orm · error · SchemaManagementException

Schema validation: missing table

Error message

Schema validation: missing table [%s]

What it means

Schema validation (hibernate.hbm2ddl.auto=validate, or SchemaValidator) compared each mapped entity table against live JDBC metadata and found no table under the entity's qualified name (tableInformation == null in validateTable). Validation is read-only and never creates anything, so any mapped table missing from the database aborts SessionFactory bootstrap with this error.

Solutions

  1. Run the schema migrations (or SchemaExport create) in that environment before startup with validate.
  2. Verify the connection URL, catalog, and hibernate.default_schema/default_catalog resolve the schema that actually holds the table.
  3. Align the mapped name with the physical table: fix @Table(name=...) or use a consistent PhysicalNamingStrategy.
  4. If the table is intentionally absent (e.g. optional module), exclude the entities from the validated metadata or drop validate for that profile.

Example fix

// before: entity expects ORDERS but PostgreSQL created orders (unquoted names are folded)
@Entity
@Table(name = "ORDERS")
public class Order { ... }

// after: match the physical name
@Entity
@Table(name = "orders")
public class Order { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Before booting with hbm2ddl.auto=validate, verify every mapped table exists via JDBC metadata
try (Connection c = dataSource.getConnection()) {
    for (String table : List.of("orders", "customers", "order_lines")) {
        try (ResultSet rs = c.getMetaData().getTables(null, null, table, new String[]{"TABLE"})) {
            if (!rs.next()) {
                throw new IllegalStateException("Migrations incomplete: table " + table + " missing - refusing to validate");
            }
        }
    }
}

Try / catch

try {
    sessionFactory = metadata.buildSessionFactory(); // with hbm2ddl.auto=validate
} catch (SchemaManagementException e) {
    if (e.getMessage() != null && e.getMessage().contains("missing table")) {
        // mapping/database drift: report the qualified name, run migrations or fix naming, then retry bootstrap once
    }
    throw e;
}

Prevention

When it happens

Trigger: SessionFactory startup with hbm2ddl.auto=validate when the extractor cannot find table.getQualifiedTableName(): the schema was never created or migrations were not run in that environment; the JDBC URL/default_schema resolves a different namespace; the physical naming strategy or @Table name produces a name that differs from the physical table (case folding on PostgreSQL, quoted vs unquoted identifiers).

Common situations: Deploying with ddl-auto=validate before Flyway/Liquibase has run; pointing at the wrong database/schema in the connection URL; changing the PhysicalNamingStrategy between releases; validating against a replica or role that cannot see the app schema; Postgres lowercasing unquoted CamelCase names at creation time while the mapping now expects CamelCase.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

			}
		}
	}

	protected abstract void validateTables(
			Metadata metadata,
			DatabaseInformation databaseInformation,
			ExecutionOptions options,
			ContributableMatcher contributableInclusionFilter,
			Dialect dialect, Namespace namespace);

	protected void validateTable(
			Table table,
			TableInformation tableInformation,
			Metadata metadata,
			ExecutionOptions options,
			Dialect dialect) {
		if ( tableInformation == null ) {
			throw new SchemaManagementException(
					String.format(
							"Schema validation: missing table [%s]",
							table.getQualifiedTableName().toString()
					)
			);
		}

		for ( var column : table.getColumns() ) {
			final var existingColumn =
					//QUESTION: should this use metadata.getDatabase().toIdentifier( column.getQuotedName() )
					tableInformation.getColumn( toIdentifier( column.getQuotedName() ) );
			if ( existingColumn == null ) {
				throw new SchemaManagementException(
						String.format(
								"Schema validation: missing column [%s] in table [%s]",
								column.getName(),
								table.getQualifiedTableName()
						)

View on GitHub (pinned to fad1729dce)