quarkusio/quarkus · error · RuntimeException
principal was null
Error message
principal was null
What it means
Sentinel RuntimeException thrown by AnnotationSecurityServlet.doGet() (also reached via doDelete) when HttpServletRequest.getUserPrincipal() returns null (so getName() would NPE) — the servlet is testing that Elytron annotation-based security populated the principal for secured paths.
Source
Thrown at integration-tests/elytron-undertow/src/main/java/io/quarkus/it/undertow/elytron/AnnotationSecurityServlet.java:35
@HttpMethodConstraint(value = "DELETE", emptyRoleSemantic = ServletSecurity.EmptyRoleSemantic.DENY),
@HttpMethodConstraint(value = "POST", rolesAllowed = "interns")
})
public class AnnotationSecurityServlet extends HttpServlet {
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (req.getUserPrincipal().getName() == null) {
throw new RuntimeException("principal was null");
}
resp.setStatus(200);
resp.addHeader("Content-Type", "text/plain");
resp.getWriter().write("hello");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (req.getUserPrincipal().getName() == null) {
throw new RuntimeException("principal was null");
}
String name = req.getReader().readLine();
resp.setStatus(200);
resp.addHeader("Content-Type", "text/plain");
resp.getWriter().write("hello " + name);
}
}
View on GitHub (pinned to e1c734241f)
Solutions
- Send the request with valid Basic Auth credentials matching the Elytron realm
- Verify @ServletSecurity / @RolesAllowed annotations are honored by the elytron-undertow extension configuration
- Check application.properties realm/user definitions for the undertow Elytron integration
- Confirm the servlet mapping's security constraint is not bypassed by a permit-all rule
Example fix
// before curl http://localhost:8080/annotation-servlet // after curl -u admin:admin http://localhost:8080/annotation-servlet
Defensive patterns
Strategy: type-guard
Validate before calling
Principal p = req.getUserPrincipal();
if (p == null) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication required");
return;
} Type guard
boolean hasPrincipal(HttpServletRequest req) {
Principal p = req.getUserPrincipal();
return p != null && p.getName() != null;
} Try / catch
try {
handleRequest(req, resp);
} catch (RuntimeException e) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "No principal attached");
} Prevention
- Null-check getUserPrincipal() before calling getName()
- Always authenticate servlet test requests with realm credentials
- Verify @ServletSecurity annotations are enforced after Undertow config changes
When it happens
Trigger: GET or DELETE request to the servlet's secured path without an authenticated principal — missing credentials or annotation-based auth not enforcing/propagating the identity into the Undertow request.
Common situations: Calling secured servlet endpoints without Basic Auth; @ServletSecurity annotations not applied (security build misconfigured); Undertow/Elytron integration not wiring the auth mechanism.
Related errors
- No producers for required item %s, step builder used: %s
- cycle detection failure report (dynamic CycleBuildException
- Build step '%s' does not produce any build item and thus wil
- Cannot consume/produce interface or abstract class build ite
- Failed to get user principal
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b3117b3793175096.
Report an issue: GitHub.