{"record":{"id":"5de19d5ba28e3b11","repo":"hibernate/hibernate-orm","slug":"could-not-find-selectable-s-in-embeddable-type","errorCode":null,"errorMessage":"Could not find selectable [%s] in embeddable type [%s] for JSON processing.","messagePattern":"Could not find selectable \\[(.+?)\\] in embeddable type \\[(.+?)\\] for JSON processing\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java","lineNumber":88,"sourceCode":"\t\t// that means that we have to stop parsing. That may be the case while parsing an object of object array,\n\t\t// the array is not empty, but we ae done parsing that specific object.\n\t\t// When we encounter OBJECT_END the current type is popped out of the stack. When parsing one object of an array we may end up\n\t\t// having an empty stack. Next Objects are parsed in the next round.\n\t\twhile(reader.hasNext() && !parseLevel.isEmpty()) {\n\t\t\tfinal ParseLevel currentLevel = parseLevel.getCurrent();\n\t\t\tassert currentLevel != null;\n\t\t\tswitch (reader.next()) {\n\t\t\t\tcase VALUE_KEY -> {\n\t\t\t\t\tfinal EmbeddableMappingType currentEmbeddableMappingType = currentLevel.embeddableMappingType;\n\t\t\t\t\tassert currentEmbeddableMappingType != null\n\t\t\t\t\t\t\t: \"Value keys are only valid for objects\";\n\n\t\t\t\t\tassert currentSelectableData == null;\n\n\t\t\t\t\tfinal String selectableName = reader.getObjectKeyName();\n\t\t\t\t\tfinal int selectableIndex = currentEmbeddableMappingType.getSelectableIndex( selectableName );\n\t\t\t\t\tif ( selectableIndex < 0 ) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\t\t\"Could not find selectable [%s] in embeddable type [%s] for JSON processing.\",\n\t\t\t\t\t\t\t\t\t\tselectableName,\n\t\t\t\t\t\t\t\t\t\tcurrentEmbeddableMappingType.getMappedJavaType().getJavaTypeClass().getName()\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tfinal SelectableMapping selectableMapping =\n\t\t\t\t\t\t\tcurrentEmbeddableMappingType.getJdbcValueSelectable( selectableIndex );\n\t\t\t\t\tcurrentSelectableData = new SelectableData( selectableName, selectableIndex, selectableMapping );\n\t\t\t\t}\n\t\t\t\tcase ARRAY_START -> {\n\t\t\t\t\tassert currentSelectableData != null;\n\n\t\t\t\t\tif ( !(currentSelectableData.selectableMapping.getJdbcMapping() instanceof BasicPluralType<?, ?> pluralType) ) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\t\t\t\"Can't parse JSON array for selectable [%s] which is not of type BasicPluralType.\",","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java#L70-L106","documentation":"When Hibernate reads a JSON-mapped embeddable (aggregate mapping), JsonHelper walks the JSON object and resolves each key against the embeddable's selectables with getSelectableIndex(name). A key with no matching selectable makes it throw IllegalArgumentException('Could not find selectable ...') - the stored JSON document and the mapped embeddable disagree about which properties exist.","triggerScenarios":"Loading an entity whose JSON column contains keys that have no corresponding attribute/column in the @Embeddable: an attribute was renamed or removed, or the JSON was written by another service, a custom JsonFormatMapper, or by hand.","commonSituations":"Schema drift between microservices sharing a JSON column; renaming an embeddable field without migrating stored documents; custom Jackson-based format mappers serializing extra properties (mixins, @JsonAnyGetter); test fixtures with hand-written JSON.","solutions":["Migrate the stored JSON documents to match the current embeddable (rename/remove the extra keys, e.g. UPDATE ... SET doc = doc - 'legacyKey' on Postgres jsonb).","Or extend the embeddable so every stored key maps to a selectable (add the missing attribute).","Configure the writing side (custom JsonFormatMapper) to emit only mapped attributes so future documents stay clean.","For JSON columns shared across applications, read them through a versioned DTO instead of the strict embeddable mapping."],"exampleFix":"// before: stored {\"name\":\"x\",\"legacyCode\":\"y\"} but embeddable maps only 'name'\n@Embeddable public class Address { String name; }\n// load -> Could not find selectable [legacyCode]\n\n// after: either migrate the data or map the extra key\n@Embeddable public class Address {\n    String name;\n    @Column(name = \"legacy_code\") String legacyCode;\n}\n// or: UPDATE entity_table SET doc = doc - 'legacyCode';","handlingStrategy":"validation","validationCode":"// Validate the document before Hibernate reads it (Jackson example)\nstatic void checkJsonKeysAgainstEmbeddable(com.fasterxml.jackson.databind.JsonNode doc,\n                                            Set<String> mappedAttributeNames) {\n    doc.fieldNames().forEachRemaining(key -> {\n        if (!mappedAttributeNames.contains(key)) {\n            throw new IllegalArgumentException(\"Unmapped JSON key in stored document: \" + key);\n        }\n    });\n}","typeGuard":null,"tryCatchPattern":"try {\n    return session.find(Product.class, id);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Could not find selectable\")) {\n        // message names the unmapped key and embeddable: migrate the JSON column or add the attribute\n    } else {\n        throw e;\n    }\n}","preventionTips":["Migrate stored JSON whenever embeddable attributes are renamed or removed","Configure the writing JsonFormatMapper to emit only mapped properties","Validate JSON documents against the mapping in a CI test with production-like data"],"tags":["hibernate","json","embeddable","aggregate-mapping","schema-drift"],"backgroundTag":"json-mapping-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}