{"record":{"id":"79504201924dc65d","repo":"hibernate/hibernate-orm","slug":"cannot-apply-countdistinct-to-predicates","errorCode":null,"errorMessage":"Cannot apply `countDistinct()` to predicates","messagePattern":"Cannot apply `countDistinct\\(\\)` to predicates","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/predicate/AbstractSqmPredicate.java","lineNumber":78,"sourceCode":"\t}\n\n\t@Nonnull\n\t@Override\n\tpublic List<Expression<Boolean>> getExpressions() {\n\t\t/// most predicates do not have sub-predicates\n\t\treturn new ArrayList<>(0);\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic JpaNumericExpression<Long> count() {\n\t\tthrow new UnsupportedOperationException( \"Cannot apply `count()` to predicates\" );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic JpaNumericExpression<Long> countDistinct() {\n\t\tthrow new UnsupportedOperationException( \"Cannot apply `countDistinct()` to predicates\" );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmBooleanExpression coalesce(@Nonnull Expression<? extends Boolean> y) {\n\t\treturn (SqmBooleanExpression) super.coalesce( y );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmBooleanExpression coalesce(Boolean y) {\n\t\treturn (SqmBooleanExpression) super.coalesce( y );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmBooleanExpression nullif(@Nonnull Expression<? extends Boolean> y) {\n\t\treturn (SqmBooleanExpression) super.nullif( y );","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/predicate/AbstractSqmPredicate.java#L60-L96","documentation":"AbstractSqmPredicate.countDistinct throws UnsupportedOperationException for the same reason as count(): COUNT(DISTINCT ...) is an aggregate over an expression producing values per row, and a predicate is a boolean condition, not a per-row value expression. The method is inherited from JpaExpression's interface surface, so it is visible on predicates, but Hibernate's predicate base class rejects it at build time.","triggerScenarios":"Calling `predicate.countDistinct()` — e.g. `cb.equal( root.get(\"status\"), \"A\" ).countDistinct()`; generic distinct-count builders applying countDistinct() to Expression<?> without type checks; attempts to emulate COUNT(DISTINCT CASE WHEN ... THEN x END) by counting the predicate itself.","commonSituations":"Distinct-count reporting code paths that handle all expressions uniformly; porting SQL like `count(distinct (a=b))` to criteria; wrapper APIs exposing countDistinct() on every node descending from JpaExpression.","solutions":["Count distinct values of a path under the predicate: `query.select( cb.countDistinct( root.get(\"x\")) ).where( predicate )`","For CASE-based distinct counts, apply countDistinct() to the case expression, not the predicate","Type-guard generic code: skip or throw your own clear error when the node is a JpaPredicate","Verify the intended SQL first — COUNT(DISTINCT <boolean>) is almost never what the requirement means"],"exampleFix":"// before\nJpaPredicate active = cb.isTrue( root.get(\"active\") );\nexpr = active.countDistinct(); // UnsupportedOperationException\n\n// after\nquery.select( cb.countDistinct( root.get(\"department\") ) ).where( active );","handlingStrategy":"type-guard","validationCode":"if (expr instanceof org.hibernate.query.criteria.JpaPredicate) {\n    throw new IllegalArgumentException(\"countDistinct() not applicable to predicates\");\n}","typeGuard":"static boolean isCountable(org.hibernate.query.criteria.JpaExpression<?> e) {\n    return !(e instanceof org.hibernate.query.criteria.JpaPredicate);\n}","tryCatchPattern":null,"preventionTips":["Use cb.countDistinct(path) in the select list with the predicate as where","Apply countDistinct to value expressions only","Guard generic aggregation wrappers with a predicate type check"],"tags":["hibernate","criteria-api","aggregate","count-distinct","predicate","unsupported-operation"],"backgroundTag":"criteria-api-misuse","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}