quarkusio/quarkus · error · IllegalStateException

Method 'TenantResolver.getDefaultTenantId()' returned a null

Error message

Method 'TenantResolver.getDefaultTenantId()' returned a null value. This violates the contract of the interface!

What it means

TenantResolver.getDefaultTenantId() must return a non-null tenant id; this is the interface contract. Quarkus throws IllegalStateException when it returns null while fetching the connection provider for a tenant connection in a multi-tenant persistence unit.

Source

Thrown at extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/HibernateMultiTenantConnectionProvider.java:58

        InstanceHandle<TenantResolver> tenantResolver = tenantResolver(persistenceUnitName);
        String tenantId;
        // Activate RequestScope if the TenantResolver is @RequestScoped or @SessionScoped
        ManagedContext requestContext = Arc.container().requestContext();
        Class<? extends Annotation> tenantScope = tenantResolver.getBean().getScope();
        boolean requiresRequestScope = (tenantScope == RequestScoped.class || tenantScope == SessionScoped.class);
        boolean forceRequestActivation = (!requestContext.isActive() && requiresRequestScope);
        try {
            if (forceRequestActivation) {
                requestContext.activate();
            }
            tenantId = tenantResolver.get().getDefaultTenantId();
        } finally {
            if (forceRequestActivation) {
                requestContext.deactivate();
            }
        }
        if (tenantId == null) {
            throw new IllegalStateException("Method 'TenantResolver.getDefaultTenantId()' returned a null value. "
                    + "This violates the contract of the interface!");
        }
        return selectConnectionProvider(tenantId);
    }

    @Override
    protected ConnectionProvider selectConnectionProvider(final String tenantIdentifier) {
        LOG.debugv("selectConnectionProvider(persistenceUnitName={0}, tenantIdentifier={1})", persistenceUnitName,
                tenantIdentifier);

        ConnectionProvider provider = providerMap.get(tenantIdentifier);
        if (provider == null) {
            final ConnectionProvider connectionProvider = resolveConnectionProvider(persistenceUnitName, tenantIdentifier);
            providerMap.put(tenantIdentifier, connectionProvider);
            return connectionProvider;
        }
        return provider;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Fix getDefaultTenantId() to always return a non-null value (provide a real default tenant)
  2. If tenant comes from the request, ensure the call happens inside an active request context or capture it earlier
  3. Throw a domain-specific exception when the tenant is truly unknown instead of returning null
  4. Add a test covering unauthenticated/no-tenant requests

Example fix

// before
@Override
public String getDefaultTenantId() {
    return httpHeaders.getHeaderString("X-Tenant"); // may be null
}

// after
@Override
public String getDefaultTenantId() {
    String tenant = httpHeaders.getHeaderString("X-Tenant");
    if (tenant == null) {
        throw new WebApplicationException("Missing tenant", 400);
    }
    return tenant;
}
Defensive patterns

Strategy: validation

Validate before calling

String tenant = resolveTenantFromContext();
if (tenant == null || tenant.isBlank()) {
    throw new IllegalStateException("Tenant id must not be null/blank before Hibernate calls");
}

Type guard

boolean validTenant(String t) { return t != null && !t.isBlank(); }

Try / catch

try {
    return session.withTransaction(s -> s.find(E.class, id)).await().indefinitely();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("getDefaultTenantId")) { logMissingTenant(); }
    throw e;
}

Prevention

When it happens

Trigger: Multi-tenant Hibernate ORM is active, getDefaultTenantId() is invoked (e.g., when the tenant connection provider must fall back to the default tenant / 'any' connection provider), and the user implementation returns null.

Common situations: Tenant id source not populated yet (e.g., reading tenant from a request header outside of an active request context, no request scope, Vert.x context absent); resolver method returns null instead of a default tenant; missing @ActivateRequestContext or context propagation.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/4e107de899c839c5. Report an issue: GitHub.