{"record":{"id":"3a4703cc169361e6","repo":"quarkusio/quarkus","slug":"cannot-call-getidentity-from-the-io-thread-when","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"exception","errorClass":"BlockingOperationNotAllowedException","httpStatus":null,"severity":"error","filePath":"extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/AbstractSecurityIdentityAssociation.java","lineNumber":47,"sourceCode":"\n    public Uni<SecurityIdentity> getDeferredIdentity() {\n        if (deferredIdentity != null) {\n            return deferredIdentity;\n        } else if (identity != null) {\n            return Uni.createFrom().item(identity);\n        } else {\n            return deferredIdentity = getIdentityProviderManager().authenticate(AnonymousAuthenticationRequest.INSTANCE);\n        }\n    }\n\n    @Override\n    public SecurityIdentity getIdentity() {\n        if (identity == null) {\n            if (deferredIdentity != null) {\n                if (BlockingOperationControl.isBlockingAllowed()) {\n                    identity = deferredIdentity.await().indefinitely();\n                } else {\n                    throw new BlockingOperationNotAllowedException(\n                            \"Cannot call getIdentity() from the IO thread when lazy authentication \" +\n                                    \"is in use, as resolving the identity may block the thread. Instead you should inject the \"\n                                    +\n                                    \"CurrentIdentityAssociation, call CurrentIdentityAssociation#getDeferredIdentity() and \" +\n                                    \"subscribe to the Uni.\");\n                }\n            }\n            if (identity == null) {\n                identity = getIdentityProviderManager().authenticate(AnonymousAuthenticationRequest.INSTANCE).await()\n                        .indefinitely();\n            }\n        }\n        return identity;\n    }\n\n}\n","sourceCodeStart":29,"sourceCodeEnd":64,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/AbstractSecurityIdentityAssociation.java#L29-L64","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Inject CurrentIdentityAssociation and use getDeferredIdentity(), chaining with Uni combinator operators instead of calling getIdentity().","Mark the endpoint or method @Blocking so it runs on a worker thread where getIdentity() may block.","Enable proactive authentication (quarkus.http.auth.proactive=true) so the identity is resolved before the endpoint runs and getIdentity() returns without blocking.","Perform the identity access inside a Uni chain and return Uni<Response> from the endpoint."],"exampleFix":"// before (reactive endpoint, IO thread)\nString user = identityAssociation.getIdentity().getPrincipal().getName();\n\n// after\nUni<String> user = identityAssociation.getDeferredIdentity()\n        .onItem().transform(id -> id.getPrincipal().getName());","handlingStrategy":"try-catch","validationCode":"// check before resolving\nif (!io.quarkus.runtime.BlockingOperationControl.isBlockingAllowed()) {\n    return identityAssociation.getDeferredIdentity(); // stay async\n}","typeGuard":"boolean canBlockGetIdentity() {\n    return io.quarkus.runtime.BlockingOperationControl.isBlockingAllowed();\n}","tryCatchPattern":"try {\n    SecurityIdentity id = association.getIdentity();\n} catch (BlockingOperationNotAllowedException e) {\n    // fall back to Uni<SecurityIdentity> = association.getDeferredIdentity()\n}","preventionTips":["Never call getIdentity() from reactive endpoints; use getDeferredIdentity() in Uni chains","Annotate worker-thread methods with @Blocking when you need blocking access to the identity","Keep quarkus.http.auth.proactive=true unless lazy auth is intentional","Avoid injecting CurrentIdentityAssociation into Vert.x event-loop code paths"],"tags":["security","reactive","event-loop","blocking-not-allowed"],"backgroundTag":"blocking-operation-not-allowed-on-io-thread","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}