{"record":{"id":"b17494af46fa752c","repo":"hibernate/hibernate-orm","slug":"unable-to-locate-parameter-s-s-for-s-s","errorCode":null,"errorMessage":"Unable to locate parameter `%s.%s` for %s - %s : %s","messagePattern":"Unable to locate parameter `(.+?)\\.(.+?)` for (.+?) - (.+?) : (.+?)","errorType":"exception","errorClass":"UnknownParameterException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/JdbcValueBindingsImpl.java","lineNumber":64,"sourceCode":"\n\t@Override\n\tpublic BindingGroup getBindingGroup(String tableName) {\n\t\tfinal String normalizedTableName = ( tableName );\n\t\treturn bindingGroupMap.get( normalizedTableName );\n\t}\n\n\t@Override\n\tpublic void bindValue(\n\t\t\tObject value,\n\t\t\tString tableName,\n\t\t\tString columnName,\n\t\t\tParameterUsage usage) {\n\t\t// Normalize column name BEFORE calling resolveValueDescriptor because\n\t\t// AbstractJdbcMutation.findValueDescriptor expects normalized names\n\t\tfinal var jdbcValueDescriptor =\n\t\t\t\tjdbcValueDescriptorAccess.resolveValueDescriptor( tableName, columnName, usage );\n\t\tif ( jdbcValueDescriptor == null ) {\n\t\t\tthrow new UnknownParameterException( mutationType, mutationTarget, tableName, columnName, usage );\n\t\t}\n\t\t// Normalize table name for storage to match cycle-breaking lookups\n\t\tfinal String physicalTableName = jdbcValueDescriptorAccess.resolvePhysicalTableName( tableName );\n\t\tresolveBindingGroup( ( physicalTableName ) )\n\t\t\t\t.bindValue( columnName, value, jdbcValueDescriptor );\n\t}\n\n\tprivate BindingGroup resolveBindingGroup(String tableName) {\n\t\tfinal var existing = bindingGroupMap.get( tableName );\n\t\tif ( existing != null ) {\n\t\t\tassert tableName.equals( existing.getTableName() );\n\t\t\treturn existing;\n\t\t}\n\t\telse {\n\t\t\tfinal var created = new BindingGroup( tableName );\n\t\t\tbindingGroupMap.put( tableName, created );\n\t\t\treturn created;\n\t\t}","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/JdbcValueBindingsImpl.java#L46-L82","documentation":"JdbcValueBindingsImpl.bindValue resolves a JdbcValueDescriptor for (tableName, columnName, usage) from the mutation's parameter descriptors; when none matches it throws UnknownParameterException formatted as 'Unable to locate parameter `table.column` for usage - MUTATIONTYPE : rolePath'. It means a value is being bound for a column that does not exist in the generated INSERT/UPDATE/DELETE parameter list for that table.","triggerScenarios":"Binding a column that is not part of the mapped mutation for that table: secondary-table (@Table/@SecondaryTable) or JOINED-inheritance mappings where the attribute/table split disagrees with the mutation SQL, custom @SQLInsert/@SQLUpdate/@SQLDelete with mismatched parameter lists, or optimizer/dynamic-update paths that drop a parameter (several were Hibernate 6 bugs).","commonSituations":"After upgrading Hibernate 6.x point releases with JOINED inheritance or secondary tables; entities with @DynamicUpdate plus soft-delete/@Where; hand-written custom SQL mutation annotations whose placeholders no longer match the mapping.","solutions":["Check the named table/column in the message against the entity mapping (typos, physical naming strategy, secondary table assignment)","Remove or align custom @SQLInsert/@SQLUpdate/@SQLDelete annotations so their parameter placeholders match the mapped columns","Reproduce with a minimal entity and report/upgrade: several 'Unable to locate parameter' bugs in JOINED/secondary-table mutation handling were fixed across 6.2-6.6 patches — move to the latest patch release","Toggle @DynamicUpdate/@DynamicInsert off to see whether the optional-mutation path changes the failure"],"exampleFix":"// before: custom SQL parameter list disagrees with mapping\n@SQLUpdate(sql = \"UPDATE doc SET content = ? WHERE id = ?\")\n\n// after: match mapped columns (incl. version/tenant columns) or drop the custom SQL\n@SQLUpdate(sql = \"UPDATE doc SET content = ?, version = ? WHERE id = ? AND version = ?\")","handlingStrategy":"validation","validationCode":"// validate custom SQL placeholders against mapped columns at boot\nfor (MappedColumn col : mappedColumns(entity)) {\n    if (!customSql.contains(col.placeholder())) {\n        throw new IllegalStateException(\"custom SQL missing parameter for \" + col.name());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    session.flush();\n}\ncatch (UnknownParameterException e) {\n    // e names table.column, usage and mutation type: compare against mapping\n    log.error(\"mapping mismatch: {}\", e.getMessage());\n}","preventionTips":["Keep custom @SQLInsert/@SQLUpdate in lockstep with mapping changes (review in PRs)","Re-test secondary-table and inheritance mappings after Hibernate upgrades","Stay on the latest 6.x patch release where binding bugs are fixed"],"tags":["mapping","parameter-binding","mutation","custom-sql","hibernate"],"backgroundTag":"missing-parameter-binding","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}