hs-web/hsweb-framework · error · IllegalArgumentException

join class [" + clazz + "] not found!

Error message

join class [" + clazz + "] not found!

What it means

DefaultQueryHelper's QuerySpec supports join-by-class: when you call join(Class) (or similar), it searches the list of already-registered joins for one whose mainClass equals the requested class. If no registered join matches, it throws this IllegalArgumentException instead of returning null, forcing the caller to fix the class reference.

Solutions

  1. Call join(TargetEntity.class) to register the join before referencing it by class.
  2. Verify the exact class (FQCN/identity) passed to the join-by-class lookup matches the class used when creating the join, including subclasses vs superclasses.
  3. Inspect the query spec's joins list (debug/toString) to confirm which classes were actually joined.
  4. If resolving dynamically, wrap the lookup and register the join lazily when the lookup fails.

Example fix

// before
QueryHelper wherever = helper.createQuery(UserEntity.class)
    .join(RoleEntity.class);
// RoleEntity was never joined -> IllegalArgumentException

// after
QuerySpec<UserEntity> spec = helper.createQuery(UserEntity.class)
    .leftJoin(RoleEntity.class, on -> on.eq("userId", "id"));
spec.join(RoleEntity.class); // now found among registered joins
Defensive patterns

Strategy: try-catch

Validate before calling

// Java
boolean joined = spec.getJoins().stream()
    .anyMatch(j -> Objects.equals(j.getMainClass(), TargetEntity.class));
if (!joined) { spec.leftJoin(TargetEntity.class, on -> on.eq("id", "targetId")); }

Try / catch

// Java
try {
    spec.join(TargetEntity.class);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("join class")) {
        spec.leftJoin(TargetEntity.class, on -> on.eq("id", "targetId"));
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling QuerySpec.join(SomeEntity.class) (or an overload resolving joins by class) where SomeEntity.class was never registered as a join — e.g. the class was never passed to a previous join() call, or a different entity class (a subclass/superclass or a different type) was actually joined.

Common situations: Typo or refactor renamed the entity class so the queried class no longer matches the joined one; developer assumes joins are implicit via entity relations but they must be declared explicitly; copy-pasted query code referencing a join from another query builder instance.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of hs-web/hsweb-framework@b2cfc85a57 (2026-09-13). Data as JSON: /api/errors/fc5e885ec662b8cb. Report an issue: GitHub.

Appendix: source

Thrown at hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/query/DefaultQueryHelper.java:535

            this.parent = parent;
            logContext = Context.of(Logger.class, LoggerFactory.getLogger(clazz));
        }

        private List<JoinConditionalSpecImpl> joins() {
            return joins == null ? joins = new ArrayList<>(3) : joins;
        }

        private JoinConditionalSpecImpl getJoinByClass(Class<?> clazz) {

            if (joins != null) {
                for (JoinConditionalSpecImpl join : joins) {
                    if (Objects.equals(join.mainClass, clazz)) {
                        return join;
                    }
                }
            }

            throw new IllegalArgumentException("join class [" + clazz + "] not found!");
        }

        private JoinConditionalSpecImpl getJoinByAlias(String alias) {
            if (joins != null) {
                for (JoinConditionalSpecImpl join : joins) {
                    if (Objects.equals(join.alias, alias)) {
                        return join;
                    }
                }
            }

            throw new IllegalArgumentException("join alias [" + alias + "] not found!");
        }

        @Override
        public <From> FromSpec<R> from(Class<From> clazz) {
            query = parent
                .database

View on GitHub (pinned to b2cfc85a57)