apache/pulsar · error · RestException
messageId is null
Error message
messageId is null
What it means
The triggerOffload admin endpoint requires a MessageIdImpl request body identifying the ledger/entry to offload. When the body is missing or null the endpoint immediately returns HTTP 400 with 'messageId is null'.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java:4037
@ApiResponse(responseCode = "405", description = "Operation is not allowed on the persistent topic"),
@ApiResponse(responseCode = "409", description = "Offload already running"),
@ApiResponse(responseCode = "412", description = "Topic name is not valid"),
@ApiResponse(responseCode = "500", description = "Internal server error"),
@ApiResponse(responseCode = "503", description = "Failed to validate global cluster configuration")})
public void triggerOffload(
@Suspended final AsyncResponse asyncResponse,
@Parameter(description = "Specify the tenant", required = true)
@PathParam("tenant") String tenant,
@Parameter(description = "Specify the namespace", required = true)
@PathParam("namespace") String namespace,
@Parameter(description = "Specify topic name", required = true)
@PathParam("topic") @Encoded String encodedTopic,
@Parameter(description = "Whether leader broker redirected this call to this broker. For internal use.")
@QueryParam("authoritative") @DefaultValue("false") boolean authoritative,
MessageIdImpl messageId) {
try {
if (messageId == null) {
throw new RestException(Response.Status.BAD_REQUEST, "messageId is null");
}
validateTopicName(tenant, namespace, encodedTopic);
internalTriggerOffload(asyncResponse, authoritative, messageId);
} catch (WebApplicationException wae) {
asyncResponse.resume(wae);
} catch (Exception e) {
asyncResponse.resume(new RestException(e));
}
}
@GET
@Path("/{tenant}/{namespace}/{topic}/offload")
@Operation(summary = "Offload a prefix of a topic to long term storage")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Offload a prefix of a topic to long term storage",
content = @Content(schema = @Schema(implementation = OffloadProcessStatus.class))),View on GitHub (pinned to 820761864e)
Solutions
- Send a JSON body like {"ledgerId":123,"entryId":4} (with partitionIndex if applicable)
- Use the admin client method admin.topics().triggerOffload(topic, messageId) which serializes correctly
- Check the request Content-Type is application/json
Example fix
// before
curl -X POST .../persistent/public/default/t/offload
// after
curl -X POST -H 'Content-Type: application/json' \
-d '{"ledgerId":4711,"entryId":0}' .../persistent/public/default/t/offload Defensive patterns
Strategy: validation
Validate before calling
if (messageId == null) throw new IllegalArgumentException("Provide a MessageId (ledgerId/entryId) to offload"); Type guard
boolean isOffloadable(MessageId id) { return id instanceof MessageIdImpl && ((MessageIdImpl) id).getLedgerId() >= 0; } Try / catch
try { admin.topics().triggerOffload(topic, messageId); } catch (PulsarAdminException e) { if (e.getStatusCode() == 400) { /* check request body */ } } Prevention
- Always send a JSON body with ledgerId/entryId for the offload endpoint
- Use the PulsarAdmin client instead of raw curl
- Set Content-Type: application/json on manual calls
When it happens
Trigger: POST /admin/v2/persistent/{tenant}/{namespace}/{topic}/offload with an empty body or JSON that does not deserialize into MessageIdImpl (e.g. wrong field names, non-JSON body).
Common situations: curl calls without -d payload; using MessageId instances of another implementation type that fail Jackson deserialization; client libs that serialize MessageId as a string instead of the ledgerId/entryId object.
Related errors
- Timeout during delete operation
- Timeout during close operation
- Timeout during open-cursor operation
- Timeout during delete-cursors operation
- The offloadPolicies must be specified for namespace offload.
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/2f7c802c6edee42b.
Report an issue: GitHub.