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
- Let the LRA coordinator invoke the callback rather than calling the endpoint directly
- Ensure the coordinator is running and configured with the correct callback URL base (quarkus.narayana.lra.*)
- Check that no proxy/load balancer strips the LRA_HTTP_CONTEXT_HEADER
- 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
- Never invoke participant callbacks directly; let the coordinator drive them
- Verify the LRA coordinator URL configuration (quarkus.narayana.lra.*)
- Ensure proxies preserve the LRA_HTTP_CONTEXT_HEADER
- Confirm @LRA annotations create an active context before cancellation
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
- seconds cannot be negative
- Unknown semantics
- Cannot specify both an exception handler and SUSPEND_EXISTIN
- Transaction already active
- Transaction already active
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/686985efa04d0cd3.
Report an issue: GitHub.