apache/druid · error · IllegalStateException

Can't find surrogate task[%s]

Error message

Can't find surrogate task[%s]

What it means

SurrogateAction delegates a TaskAction to a 'surrogate' task identified by surrogateId (e.g. when a supervisor-like task performs actions on behalf of another task). Before delegating, it looks the surrogate task up in task storage; if no task with that ID exists in storage, it throws this IllegalStateException. This is an internal consistency check: the caller referenced a task ID that the overlord has no record of.

Source

Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/actions/SurrogateAction.java:72

  public ActionType getTaskAction()
  {
    return taskAction;
  }

  @Override
  public TypeReference<ReturnType> getReturnTypeReference()
  {
    return taskAction.getReturnTypeReference();
  }

  @Override
  public ReturnType perform(Task task, TaskActionToolbox toolbox)
  {
    final Optional<Task> maybeSurrogateTask = toolbox.getTaskStorage().getTask(surrogateId);
    if (maybeSurrogateTask.isPresent()) {
      return taskAction.perform(maybeSurrogateTask.get(), toolbox);
    } else {
      throw new ISE("Can't find surrogate task[%s]", surrogateId);
    }
  }

  @Override
  public String toString()
  {
    return "SurrogateAction{" +
           "surrogateId='" + surrogateId + '\'' +
           ", taskAction=" + taskAction +
           '}';
  }
}

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Check that the surrogate task ID actually exists: query the overlord /druid/indexer/v1/tasks or task storage DB for the ID in the message.
  2. Re-run or resubmit the parent task so a fresh surrogate task is created before its actions are performed.
  3. If task cleanup is killing tasks too aggressively, increase task retention (keepAlive / kill task config) so dependent actions finish first.
  4. Verify custom code isn't fabricating or truncating surrogate task IDs; upgrade if this is a known Druid bug.

Example fix

// before
TaskActionClient client = toolbox.getTaskActionClient();
client.submit(new SurrogateAction<>(someStaleId, new LockListAction()));
// after
if (toolbox.getTaskStorage().getTask(surrogateId).isPresent()) {
  client.submit(new SurrogateAction<>(surrogateId, new LockListAction()));
} else {
  LOG.warn("surrogate task %s no longer exists, skipping", surrogateId);
}
Defensive patterns

Strategy: validation

Validate before calling

Optional<Task> surrogate = toolbox.getTaskStorage().getTask(surrogateId);
if (!surrogate.isPresent()) {
  throw new IllegalStateException("Surrogate task " + surrogateId + " does not exist; resubmit the parent task");
}

Type guard

boolean surrogateExists(String id, TaskStorage storage) {
  return storage.getTask(id).isPresent();
}

Try / catch

try {
  return actionClient.submit(new SurrogateAction<>(surrogateId, action));
} catch (IllegalStateException e) {
  if (e.getMessage().startsWith("Can't find surrogate task")) {
    // recreate/resubmit the parent task, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: Submitting a surrogate TaskAction whose surrogateId refers to a task that was never created, has been killed/pruned from task storage, or whose ID was mistyped. Also occurs if the surrogate task completed and was removed before the delegating action ran.

Common situations: Overlord task storage cleaned up (e.g. kill tasks / short retention) while dependent tasks still reference old surrogate IDs; race between surrogate task completion and a queued action; bugs in custom task code forwarding wrong IDs after upgrades.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/7df20a51f008f1ea. Report an issue: GitHub.