{"record":{"id":"0988bcfdf79031a6","repo":"hibernate/hibernate-orm","slug":"table-table-has-no-column-named-column-m","errorCode":null,"errorMessage":"Table '${table}' has no column named '${column}' matching the column specified in '@Index'","messagePattern":"Table '(.+?)' has no column named '(.+?)' matching the column specified in '@Index'","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/NaturalIdBinder.java","lineNumber":78,"sourceCode":"\t\t\tfor ( Selectable selectable : property.getValue().getSelectables() ) {\n\t\t\t\tif ( selectable instanceof org.hibernate.mapping.Column column) {\n\t\t\t\t\tuniqueKey.addColumn( tableColumn( column, table, collector ) );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfor ( var column : columns.getColumns() ) {\n\t\t\t\tuniqueKey.addColumn( tableColumn( column.getMappingColumn(), table, collector ) );\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static org.hibernate.mapping.Column tableColumn(\n\t\t\torg.hibernate.mapping.Column column, Table table, InFlightMetadataCollector collector) {\n\t\tfinal String columnName = collector.getLogicalColumnName( table, column.getQuotedName() );\n\t\tfinal var tableColumn = table.getColumn( collector, columnName );\n\t\tif ( tableColumn == null ) {\n\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\"Table '\" + table.getName() + \"' has no column named '\" + columnName\n\t\t\t\t\t\t\t+ \"' matching the column specified in '@Index'\"\n\t\t\t);\n\t\t}\n\t\treturn tableColumn;\n\t}\n\n\tprivate static class NaturalIdNameSource implements ImplicitUniqueKeyNameSource {\n\t\tprivate final Table table;\n\t\tprivate final MetadataBuildingContext context;\n\n\t\tNaturalIdNameSource(Table table, MetadataBuildingContext context) {\n\t\t\tthis.table = table;\n\t\t\tthis.context = context;\n\t\t}\n\n\t\t@Override\n\t\tpublic Identifier getTableName() {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NaturalIdBinder.java#L60-L96","documentation":"While binding a natural-id unique key (and index-like column references), Hibernate resolves each specified column name to a logical column on the target table. If the logical name matches no mapped column of that table, the mapping cannot be built and AnnotationException is thrown at bootstrap. Note the lookup uses the logical column name, so physical naming strategies and @Column renames affect what must be written in the annotation.","triggerScenarios":"A @NaturalId / index column specification referencing a column name that does not exist among the entity's mapped columns: misspelled name in columnNames, a @Column(name=...) rename that the index entry was not updated to follow, quoting differences, or an implicit naming strategy that produces a different logical name than the one written in the annotation.","commonSituations":"Renaming a column in the entity mapping but not in the accompanying index/unique-key annotation; switching ImplicitNamingStrategy or PhysicalNamingStrategy (e.g. Spring's CamelCaseToUnderscoresNamingStrategy) after annotations were written; hand-written column names that assume the DB schema names rather than logical mapping names; migrations between Hibernate versions with changed default naming.","solutions":["Compare the names in the index/unique-key annotation against the actual @Column(name=...) values (or default property names) of the entity and fix the typo.","Remember the lookup uses LOGICAL column names: reference the name from the mapping (e.g. @Column(name=\"user_email\") => \"user_email\"), not a strategy-transformed physical name.","If a naming strategy transforms names, either align the annotation values with the strategy output or make the mapping names explicit with @Column so both sides agree.","Enable DEBUG logging for org.hibernate.boot.model to see the logical name Hibernate resolved for each column."],"exampleFix":"// before: column was renamed in the mapping but not in the index\n@Column(name = \"user_email\")\nString email;\n@TableIndex(name = \"ix_email\", columnNames = \"email\")  // no such logical column\n\n// after\n@TableIndex(name = \"ix_email\", columnNames = \"user_email\")","handlingStrategy":"validation","validationCode":"// Before startup, cross-check index/natural-id column names against mapped @Column names\nMap<String, Set<String>> tableColumns = readMappedColumnsPerTable(annotatedClasses); // from @Table + @Column\nfor (Class<?> entity : annotatedClasses) {\n    TableIndex idx = entity.getAnnotation(TableIndex.class);\n    if (idx != null) {\n        Set<String> mapped = tableColumns.getOrDefault(tableOf(entity), Set.of());\n        for (String c : idx.columnNames()) {\n            if (!mapped.contains(c)) throw new IllegalStateException(\"Index column '\" + c + \"' not mapped on \" + entity.getName());\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    SessionFactory sf = cfg.buildSessionFactory();\n} catch (AnnotationException e) {\n    // mapping errors: report and fail startup; retrying cannot help\n    throw new IllegalStateException(\"Mapping invalid: \" + e.getMessage(), e);\n}","preventionTips":["Update index/natural-id column strings whenever a @Column name changes","Reference logical mapping names, not naming-strategy-transformed physical names","Add a startup smoke test that builds the SessionFactory in CI to catch mapping drift"],"tags":["hibernate","jpa","natural-id","index","column-mapping","naming-strategy","bootstrap"],"backgroundTag":"column-mapping-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}