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

  1. Send the request with valid Basic Auth credentials matching the Elytron realm
  2. Verify @ServletSecurity / @RolesAllowed annotations are honored by the elytron-undertow extension configuration
  3. Check application.properties realm/user definitions for the undertow Elytron integration
  4. 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

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


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