quarkusio/quarkus · error · BlockingOperationNotAllowedException

Cannot call getIdentity() from the IO thread when lazy authe

Error message

Cannot call getIdentity() from the IO thread when lazy authentication is in use, as resolving the identity may block the thread. Instead you should inject the CurrentIdentityAssociation, call CurrentIdentityAssociation#getDeferredIdentity() and subscribe to the Uni.

What it means

Quarkus's AbstractSecurityIdentityAssociation.getIdentity() blocks waiting on the deferred authentication Uni. When called on the Vert.x IO (event-loop) thread with lazy authentication in use, blocking is forbidden, so it throws BlockingOperationNotAllowedException. The API expects async consumers to use getDeferredIdentity() and subscribe to the Uni instead.

Source

Thrown at extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/AbstractSecurityIdentityAssociation.java:47

    public Uni<SecurityIdentity> getDeferredIdentity() {
        if (deferredIdentity != null) {
            return deferredIdentity;
        } else if (identity != null) {
            return Uni.createFrom().item(identity);
        } else {
            return deferredIdentity = getIdentityProviderManager().authenticate(AnonymousAuthenticationRequest.INSTANCE);
        }
    }

    @Override
    public SecurityIdentity getIdentity() {
        if (identity == null) {
            if (deferredIdentity != null) {
                if (BlockingOperationControl.isBlockingAllowed()) {
                    identity = deferredIdentity.await().indefinitely();
                } else {
                    throw new BlockingOperationNotAllowedException(
                            "Cannot call getIdentity() from the IO thread when lazy authentication " +
                                    "is in use, as resolving the identity may block the thread. Instead you should inject the "
                                    +
                                    "CurrentIdentityAssociation, call CurrentIdentityAssociation#getDeferredIdentity() and " +
                                    "subscribe to the Uni.");
                }
            }
            if (identity == null) {
                identity = getIdentityProviderManager().authenticate(AnonymousAuthenticationRequest.INSTANCE).await()
                        .indefinitely();
            }
        }
        return identity;
    }

}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Inject CurrentIdentityAssociation and use getDeferredIdentity(), chaining with Uni combinator operators instead of calling getIdentity().
  2. Mark the endpoint or method @Blocking so it runs on a worker thread where getIdentity() may block.
  3. Enable proactive authentication (quarkus.http.auth.proactive=true) so the identity is resolved before the endpoint runs and getIdentity() returns without blocking.
  4. Perform the identity access inside a Uni chain and return Uni<Response> from the endpoint.

Example fix

// before (reactive endpoint, IO thread)
String user = identityAssociation.getIdentity().getPrincipal().getName();

// after
Uni<String> user = identityAssociation.getDeferredIdentity()
        .onItem().transform(id -> id.getPrincipal().getName());
Defensive patterns

Strategy: try-catch

Validate before calling

// check before resolving
if (!io.quarkus.runtime.BlockingOperationControl.isBlockingAllowed()) {
    return identityAssociation.getDeferredIdentity(); // stay async
}

Type guard

boolean canBlockGetIdentity() {
    return io.quarkus.runtime.BlockingOperationControl.isBlockingAllowed();
}

Try / catch

try {
    SecurityIdentity id = association.getIdentity();
} catch (BlockingOperationNotAllowedException e) {
    // fall back to Uni<SecurityIdentity> = association.getDeferredIdentity()
}

Prevention

When it happens

Trigger: Calling SecurityIdentityAssociation.getIdentity() (or CurrentIdentityAssociation.getIdentity()) directly from an event-loop context — e.g. inside a reactive REST endpoint, Vert.x route handler, or custom SecurityContextAugmentor/IdentityProvider callback running on the IO thread — while deferred identity was set via setIdentity(Uni).

Common situations: Injecting CurrentIdentityAssociation into a reactive (non-blocking) endpoint and calling getIdentity() synchronously; writing custom filters on the event loop; switching an app from proactive auth to lazy auth (quarkus.http.auth.proactive=false) where previously-working synchronous getIdentity() calls now run on the IO thread.

Understand the failure class

Related errors


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