quarkusio/quarkus · error · NullPointerException

lraId can't be null as it should be invoked with the context

Error message

lraId can't be null as it should be invoked with the context

What it means

The @Compensate LRA participant callback throws NullPointerException when the LRA_HTTP_CONTEXT_HEADER is absent. The MicroProfile LRA spec guarantees this header is present when the coordinator invokes a compensation callback, so a null means the method was called outside an LRA context (misdirected request or coordinator misconfiguration).

Source

Thrown at integration-tests/narayana-lra/src/main/java/io/quarkus/it/lra/TransactionalResource.java:68

    @Path("completions")
    public int completions() {
        return completions.get();
    }

    // return the number of times the compensation callback was called
    @GET
    @Path("compensations")
    public int compensations() {
        return compensations.get();
    }

    // callback to inform the participant service that the LRA is cancelling
    @PUT
    @Path("compensate")
    @Compensate
    public Response compensateWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) {
        if (lraId == null) {
            throw new NullPointerException("lraId can't be null as it should be invoked with the context");
        }

        compensations.incrementAndGet();

        return Response.ok(lraId.toASCIIString()).build();
    }

    // callback to inform the participant service that the LRA is closing
    @PUT
    @Path("complete")
    @Complete
    public Response completeWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) {
        if (lraId == null) {
            throw new NullPointerException("lraId can't be null as it should be invoked with the context");
        }

        completions.incrementAndGet();

View on GitHub (pinned to e1c734241f)

Solutions

  1. Let the LRA coordinator invoke the callback rather than calling the endpoint directly
  2. Ensure the coordinator is running and configured with the correct callback URL base (quarkus.narayana.lra.*)
  3. Check that no proxy/load balancer strips the LRA_HTTP_CONTEXT_HEADER
  4. Verify @LRA annotations on the triggering resource so an active LRA context exists to cancel

Example fix

// before
curl -X PUT http://host/lra/compensate   // missing LRA_HTTP_CONTEXT_HEADER
// after
# cancel the LRA via the coordinator / end the @LRA resource so the callback is invoked with the header
Defensive patterns

Strategy: validation

Validate before calling

URI lraIdHeader = /* request header */ headers.getFirst(LRA_HTTP_CONTEXT_HEADER);
if (lraIdHeader == null) {
    return Response.status(412).entity("missing LRA context").build();
}
// proceed with compensate

Type guard

static boolean hasLraContext(jakarta.ws.rs.core.HttpHeaders h) {
    return h.getHeaderString(LRA_HTTP_CONTEXT_HEADER) != null;
}

Try / catch

try {
    compensateWork(lraId);
} catch (NullPointerException e) {
    return Response.status(412, "LRA context header missing").build();
}

Prevention

When it happens

Trigger: A PUT request to /compensate without the Long-Running-Action header — e.g. manual invocation, test harness calling the endpoint directly, or the coordinator cancelling an LRA without propagating context.

Common situations: Calling the participant endpoint by hand (curl/tests) instead of through the LRA coordinator; proxy stripping custom headers; LRA coordinator misconfigured so callbacks lack context; wrong @Compensate/@LRA wiring on the endpoint.

Related errors


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