apache/druid · error · BadRequestException
Requested taskId[ ] doesn't match with taskId[ ]
Error message
Requested taskId[%s] doesn't match with taskId[%s]
What it means
ChatHandlerResource.doTaskChat is the HTTP entry point that routes chat requests to locally-hosted task handlers. Callers may set the TASK_ID_HEADER to assert they are talking to the intended task; if the header's taskId does not match the taskId in the request URL (after URL-decoding), the resource rejects the request with a 400 BadRequestException.
Solutions
- Ensure the caller sends the exact taskId of the task being targeted in the TASK_ID_HEADER, URL-encoding it for task IDs created by Druid >= 0.14.0
- Re-fetch the current taskId from the overlord/tasks API if the task was restarted and retry
- Verify the request is going to the correct peon port for this task, not another task on the same host
- Remove the header entirely if identity assurance is not needed — the check is skipped when TASK_ID_HEADER is absent
Example fix
// before
request.header("X-Druid-TASK-ID", rawTaskId); // may mismatch if URL-encoding differs
// after
request.header("X-Druid-TASK-ID", URLEncoder.encode(taskId, StandardCharsets.UTF_8)); Defensive patterns
Strategy: validation
Validate before calling
String expected = URLEncoder.encode(taskId, StandardCharsets.UTF_8);
if (requestTaskId != null
&& !requestTaskId.equals(taskId)
&& !URLDecoder.decode(requestTaskId, StandardCharsets.UTF_8).equals(taskId)) {
throw new IllegalStateException("taskId mismatch: " + requestTaskId);
} Type guard
boolean taskIdMatches(String headerValue, String pathTaskId) {
return headerValue == null
|| headerValue.equals(pathTaskId)
|| URLDecoder.decode(headerValue, StandardCharsets.UTF_8).equals(pathTaskId);
} Try / catch
try {
Response r = client.target(chatUrl).request().header("X-Druid-TASK-ID", encodedTaskId).get();
} catch (BadRequestException e) {
refreshTaskIdAndRetry(); // fetch current taskId from overlord
} Prevention
- Always URL-encode the taskId header for tasks created by Druid >= 0.14.0
- Re-fetch taskId from the overlord after any task restart
- Confirm the request targets the correct peon port for that task
When it happens
Trigger: Sending an HTTP request to a task's chat endpoint with a X-Druid-TASK-ID header whose value differs from the taskId in the request path — e.g. stale taskId from a retried task, an unencoded or wrongly encoded taskId (tasks from before 0.14.0 are not URL-encoded), or hitting the wrong peon port hosting a different task.
Common situations: Client code built for Druid < 0.14.0 sending non-URL-encoded task IDs while the server expects encoded ones; custom tooling reusing an old taskId after task restart; misrouted requests when multiple tasks share a host.
Related errors
- Action [ ] failed for worker [ ] with status ( )
- An external HTTP table with a URI must also provide the…
- An external HTTP table with a URI must also provide the…
- AuthenticationToken ignored:
- <authResult.getErrorMessage()>
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/57b4452b995b2777.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/main/java/org/apache/druid/segment/realtime/ChatHandlerResource.java:67
this.handlers = handlers;
this.taskId = taskHolder.getTaskId();
}
@Path("/{id}")
public Object doTaskChat(@PathParam("id") String handlerId, @Context HttpHeaders headers)
{
if (taskId != null) {
final List<String> requestTaskIds = headers.getRequestHeader(TASK_ID_HEADER);
final String requestTaskId = requestTaskIds != null && !requestTaskIds.isEmpty()
? Iterables.getOnlyElement(requestTaskIds)
: null;
// Sanity check: Callers set TASK_ID_HEADER to our taskId (URL-encoded, if >= 0.14.0) if they want to be
// assured of talking to the correct task, and not just some other task running on the same port.
if (requestTaskId != null
&& !requestTaskId.equals(taskId)
&& !StringUtils.urlDecode(requestTaskId).equals(taskId)) {
throw new BadRequestException(
StringUtils.format("Requested taskId[%s] doesn't match with taskId[%s]", requestTaskId, taskId)
);
}
}
final Optional<ChatHandler> handler = handlers.get(handlerId);
if (handler.isPresent()) {
return handler.get();
}
// Return HTTP 503 so SpecificTaskRetryPolicy retries in case the handler is not registered yet or has been registered before shutdown.
throw new ServiceUnavailableException(StringUtils.format("Can't find chatHandler for handler[%s]", handlerId));
}
}
View on GitHub (pinned to 9b90983fd2)