{"record":{"id":"d1d61f00e8bdadca","repo":"quarkusio/quarkus","slug":"http-security-policy-applied-only-on-quarkus-rest","errorCode":null,"errorMessage":"HTTP Security policy applied only on Quarkus REST cannot be run as 'RoutingContext' is null","messagePattern":"HTTP Security policy applied only on Quarkus REST cannot be run as 'RoutingContext' is null","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityContext.java","lineNumber":93,"sourceCode":"        // and write to a volatile variable during the request; the EagerSecurityHandler is created for each\n        // endpoint (in case there is HTTP permission configured), so there can be a lot of them\n        instance = this;\n    }\n\n    Uni<SecurityIdentity> getDeferredIdentity() {\n        return Uni.createFrom().deferred(new Supplier<Uni<? extends SecurityIdentity>>() {\n            @Override\n            public Uni<SecurityIdentity> get() {\n                return identityAssociation.get().getDeferredIdentity();\n            }\n        });\n    }\n\n    Uni<SecurityIdentity> getPermissionCheck(ResteasyReactiveRequestContext requestContext, SecurityIdentity identity,\n            MethodDescription invokedMethodDesc) {\n        final RoutingContext routingContext = requestContext.unwrap(RoutingContext.class);\n        if (routingContext == null) {\n            throw new IllegalStateException(\n                    \"HTTP Security policy applied only on Quarkus REST cannot be run as 'RoutingContext' is null\");\n        }\n        record SecurityCheckWithIdentity(SecurityIdentity identity, HttpSecurityPolicy.CheckResult checkResult) {\n        }\n        return jaxRsPathMatchingPolicy\n                .checkPermission(routingContext, identity == null ? getDeferredIdentity() : Uni.createFrom().item(identity),\n                        invokedMethodDesc)\n                .flatMap(new Function<HttpSecurityPolicy.CheckResult, Uni<? extends SecurityCheckWithIdentity>>() {\n                    @Override\n                    public Uni<SecurityCheckWithIdentity> apply(HttpSecurityPolicy.CheckResult checkResult) {\n                        if (identity != null) {\n                            return Uni.createFrom().item(new SecurityCheckWithIdentity(identity, checkResult));\n                        }\n                        if (checkResult.isPermitted() && checkResult.getAugmentedIdentity() == null) {\n                            return Uni.createFrom().item(new SecurityCheckWithIdentity(null, checkResult));\n                        }\n                        // we need to resolve identity either to compare augmented identity or to determine\n                        // whether the identity is anonymous (determines thrown exception for denied access)","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityContext.java#L75-L111","documentation":"The eager HTTP security path-matching policy (quarkus.http.auth.permission.* policies applied via Quarkus REST's JaxRsPathMatchingPolicy) requires access to the Vert.x RoutingContext. getPermissionCheck unwraps it from the request context; if absent, the check cannot run and IllegalStateException is thrown.","triggerScenarios":"An HTTP permission policy (quarkus.http.auth.permission...) whose policy applies only on Quarkus REST is evaluated for a request whose ResteasyReactiveRequestContext cannot be unwrapped to a RoutingContext — e.g. the security check runs outside a normal Vert.x HTTP request flow.","commonSituations":"Invoking resource methods through non-HTTP dispatch paths or tests without a Vert.x context; custom security checks reusing EagerSecurityContext outside the standard request pipeline; misordered handlers stripping the context.","solutions":["Ensure the security check only runs within a normal HTTP request handled by Quarkus REST (a RoutingContext is present)","Do not invoke resource methods secured by HTTP permission policies outside the Vert.x request pipeline (e.g. in unit tests use QuarkusTest with a real HTTP call)","Reorder custom handlers so the Vert.x unwrap is available when eager security runs","If you don't need path-based HTTP permissions for this endpoint, scope the quarkus.http.auth.permission paths away from it"],"exampleFix":"// test: call over HTTP instead of invoking the method directly\n// before\nnew MyResource().secured();\n// after\ngiven().when().get(\"/secured\").then().statusCode(403);\n","handlingStrategy":"try-catch","validationCode":"// before running the check, confirm a RoutingContext is available\nRoutingContext rc = requestContext.unwrap(RoutingContext.class);\nif (rc == null) {\n    // skip HTTP-policy check or route the call through a real HTTP request\n}","typeGuard":"RoutingContext rc = requestContext.unwrap(RoutingContext.class);\nboolean hasRoutingContext = rc != null;","tryCatchPattern":"try {\n    return securityContext.getPermissionCheck(requestContext, identity, methodDesc);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"RoutingContext\")) {\n        // fall back to a policy not tied to RoutingContext or reject\n    } else throw e;\n}","preventionTips":["Test secured endpoints over real HTTP calls (QuarkusTest + REST client), not direct method calls","Don't reuse eager security handlers outside the Vert.x request pipeline","Scope HTTP permission policies to endpoints actually served over HTTP"],"tags":["quarkus","security","vertx","routing-context","resteasy-reactive"],"backgroundTag":"null-routing-context-security","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}