{"record":{"id":"796adf142af49ebf","repo":"hibernate/hibernate-orm","slug":"argument-s-of-fk-function-is-not-a-single-v-796adf","errorCode":null,"errorMessage":"Argument '%s' of 'fk()' function is not a single-valued association","messagePattern":"Argument '(.+?)' of 'fk\\(\\)' function is not a single-valued association","errorType":"exception","errorClass":"FunctionArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java","lineNumber":931,"sourceCode":"\n\t@Override @SuppressWarnings(\"unchecked\")\n\tpublic <T extends HibernateCriteriaBuilder> T unwrap(Class<T> clazz) {\n\t\tfinal T result = (T) extensions.get( clazz );\n\t\tif ( result == null ) {\n\t\t\tthrow new IllegalArgumentException( \"Unable to unwrap to \" + clazz.getName() );\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic SqmPath<?> fk(Path<?> path) {\n\t\tfinal var sqmPath = (SqmPath<?>) path;\n\t\tfinal var toOneReference = sqmPath.getReferencedPathSource();\n\t\tfinal boolean validToOneRef =\n\t\t\t\ttoOneReference.getBindableType() == Bindable.BindableType.SINGULAR_ATTRIBUTE\n\t\t\t\t\t\t&& toOneReference instanceof EntitySqmPathSource;\n\t\tif ( !validToOneRef ) {\n\t\t\tthrow new FunctionArgumentException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Argument '%s' of 'fk()' function is not a single-valued association\",\n\t\t\t\t\t\t\tsqmPath.getNavigablePath()\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\treturn new SqmFkExpression<>( sqmPath );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic <X, T extends X> SqmPath<T> treat(@Nonnull Path<X> path, @Nonnull Class<T> type) {\n\t\treturn ( (SqmPath<X>) path ).treatAs( type );\n\t}\n\n\t@Nonnull\n\t@Override","sourceCodeStart":913,"sourceCodeEnd":949,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java#L913-L949","documentation":"The Hibernate-specific fk(Path) criteria function extracts the raw foreign-key column(s) of an association path, and is only defined for single-valued associations (@ManyToOne/@OneToOne). The builder validates that the path's referenced path source is a SINGULAR_ATTRIBUTE and an EntitySqmPathSource; anything else (a basic attribute, a plural attribute, a component/embeddable, a computed expression) throws FunctionArgumentException.","triggerScenarios":"cb.fk(root.get(\"status\")) where status is a String/@Enumerated basic attribute; cb.fk(root.get(\"lineItems\")) on an @OneToMany collection; cb.fk(orderRoot) on the entity root itself; cb.fk(root.get(\"address\")) where address is an @Embedded component rather than an association.","commonSituations":"Optimizing joins on FK columns without a join (the typical use: cb.fk(order.get(\"customer\").get(\"id\")) style comparisons) and mistyping the attribute; assuming fk() works on embeddables; copying HQL 'fk(...)' snippets into criteria code against the wrong attribute.","solutions":["Point fk() at the association attribute itself, e.g. cb.fk(orderRoot.get(\"customer\")) for @ManyToOne Customer customer.","For basic/composite values you wanted the value, not the FK: use the plain path (root.get(\"status\")) or root.get(\"address\").get(\"city\").","For collections, first join them (orderRoot.join(\"lineItems\")) and then navigate from the joined entity."],"exampleFix":"// before\nExpression<Long> fk = cb.fk(orderRoot.get(\"warehouseCode\")); // basic String attribute -> FunctionArgumentException\n\n// after\n// 'customer' is @ManyToOne Customer\nExpression<Long> fk = cb.fk(orderRoot.get(\"customer\")); // FK of the association","handlingStrategy":"type-guard","validationCode":"boolean isSingleValuedAssociation(Path<?> path) {\n    return path.getModel() instanceof SingularAttribute<?, ?> sa\n            && (sa.getType() instanceof EntityType<?> || sa.getPersistentAttributeType()\n                 == Attribute.PersistentAttributeType.MANY_TO_ONE\n                 || sa.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE);\n}","typeGuard":"static boolean fkApplicable(Path<?> p) {\n    if (!(p.getModel() instanceof SingularAttribute<?, ?> sa)) return false;\n    return switch (sa.getPersistentAttributeType()) {\n        case MANY_TO_ONE, ONE_TO_ONE -> true;\n        default -> false;\n    };\n}","tryCatchPattern":"try {\n    Expression<Long> fk = cb.fk(orderRoot.get(\"customer\"));\n} catch (FunctionArgumentException e) {\n    // attribute is not an association: use the plain value path instead\n    Expression<?> v = orderRoot.get(\"customerRef\");\n}","preventionTips":["Only call fk() on @ManyToOne/@OneToOne attribute paths.","For basic values use the attribute path directly; for embeddables navigate into their fields.","Double-check the attribute name string — fk() failures are frequently just wrong attribute names."],"tags":["hibernate","criteria","fk-function","association","function-argument"],"backgroundTag":"invalid-function-argument","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}