{"record":{"id":"d1ec27f5d0d84a94","repo":"hibernate/hibernate-orm","slug":"cannot-interpret-natural-id-value-s-as-compound","errorCode":null,"errorMessage":"Cannot interpret natural id value [%s] as compound natural id of entity '%s'","messagePattern":"Cannot interpret natural id value \\[(.+?)\\] as compound natural id of entity '(.+?)'","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java","lineNumber":111,"sourceCode":"\t\treturn doLoad( entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue) );\n\t}\n\n\t/**\n\t * Verify that the given natural id is \"simple\".\n\t * <p>\n\t * We allow compound natural id \"simple\" loading if all the values are passed as an array,\n\t * list, or map. We assume an array is properly ordered following the attribute ordering.\n\t * For lists, just like arrays, we assume the user has ordered them properly; for maps,\n\t * the key is expected to be the attribute name.\n\t */\n\tprivate void verifySimplicity(Object naturalIdValue) {\n\t\tassert naturalIdValue != null;\n\t\tif ( !hasSimpleNaturalId\n\t\t\t\t&& !naturalIdValue.getClass().isArray()\n\t\t\t\t&& !(naturalIdValue instanceof List)\n\t\t\t\t&& !(naturalIdValue instanceof Map)\n\t\t\t\t&& ! ( isNaturalIdClass( naturalIdValue ) ) ) {\n\t\t\tthrow new HibernateException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Cannot interpret natural id value [%s] as compound natural id of entity '%s'\",\n\t\t\t\t\t\t\tnaturalIdValue,\n\t\t\t\t\t\t\tentityPersister().getEntityName()\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate boolean isNaturalIdClass(Object naturalIdValue) {\n\t\tfinal EntityPersister entityPersister = entityPersister();\n\t\treturn entityPersister.getNaturalIdMapping().getNaturalIdClass().isInstance(  naturalIdValue );\n\t}\n\n\t@Override\n\tpublic Optional<T> loadOptional(Object naturalIdValue) {\n\t\treturn Optional.ofNullable( load( naturalIdValue ) );","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java#L93-L129","documentation":"SimpleNaturalIdLoadAccess accepts a single value per load. verifySimplicity throws when the entity has a compound natural id and the supplied value is not one of the supported carriers for multiple values: an ordered array or List, a Map keyed by attribute name, or an instance of the natural-id class.","triggerScenarios":"session.bySimpleNaturalId(Employee.class).load(\"abc\") where Employee declares @NaturalId on two or more fields; passing a scalar or an arbitrary POJO that is neither array, List, Map, nor the natural-id class.","commonSituations":"A second @NaturalId attribute is added later and breaks existing simple loads; teams unaware of the array/List/Map convention for compound ids; passing a DTO instead of the natural-id value object.","solutions":["Switch to the explicit compound API: session.byNaturalId(Employee.class).using(\"email\", e).using(\"dept\", d).load().","Or pass all values at once: an ordered List/array matching attribute order, or a Map keyed by attribute names.","If the natural id is meant to be single-valued, remove the extra @NaturalId attribute."],"exampleFix":"// before: compound natural id (email + department) loaded simply\nEmployee e = session.bySimpleNaturalId(Employee.class).load(\"john@acme.com\");\n\n// after\nEmployee e = session.byNaturalId(Employee.class)\n        .using(\"email\", \"john@acme.com\")\n        .using(\"department\", \"ENG\")\n        .load();","handlingStrategy":"validation","validationCode":"static boolean isSimpleNaturalId( Class<?> entity ) {\n    return Arrays.stream( entity.getDeclaredFields() )\n            .filter( f -> f.isAnnotationPresent( NaturalId.class ) )\n            .count() == 1;\n}\n// use bySimpleNaturalId only when isSimpleNaturalId(entity) returns true","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Wrap natural-id lookups in a repository method that knows the entity's key shape","Document the array/List/Map convention for compound natural ids loaded via the simple API","Add a mapping test asserting the number of @NaturalId attributes per entity"],"tags":["hibernate","natural-id","api-misuse"],"backgroundTag":"natural-id-mapping-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}