apache/druid · error · IllegalStateException

Lock interval[%s] != task interval[%s]

Error message

Lock interval[%s] != task interval[%s]

What it means

Thrown by AbstractFixedIntervalTask.getAndCheckLock when the lock the task actually holds does not match the task's declared interval. Druid tasks acquire a time-interval lock (via TaskActionClient) before writing; if the lock interval differs from getInterval(), the task's writes would fall outside the locked range, so Druid fails fast with an IllegalStateException to protect segment consistency.

Source

Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/task/AbstractFixedIntervalTask.java:115

    return interval;
  }

  @Override
  public void stopGracefully(TaskConfig taskConfig)
  {
  }

  TaskLock getAndCheckLock(TaskToolbox toolbox) throws IOException
  {
    // Confirm we have a lock (will throw if there isn't exactly one element)
    final TaskLock myLock = Iterables.getOnlyElement(getTaskLocks(toolbox.getTaskActionClient()));

    if (!myLock.getDataSource().equals(getDataSource())) {
      throw new ISE("Lock dataSource[%s] != task dataSource[%s]", myLock.getDataSource(), getDataSource());
    }

    if (!myLock.getInterval().equals(getInterval())) {
      throw new ISE("Lock interval[%s] != task interval[%s]", myLock.getInterval(), getInterval());
    }
    return myLock;
  }
}

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Verify the task's configured interval exactly matches the interval of the lock granted by the overlord (compare task JSON 'interval' with the lock in the tasks/locks UI).
  2. Recreate/respawn the task after any change to the datasource or interval so locks are re-acquired for the new interval.
  3. If using a custom task, ensure the TaskLock acquired in the run() path uses the same Interval object returned by getInterval().
  4. Check for interval normalization (e.g., granularity truncation) that changes the interval between lock request and lock check.

Example fix

// before
TaskLock lock = taskActionClient.submit(new LockTryAcquireAction(new TaskLock("ds", new Interval("2023/2024"), "p")));
// task configured with Interval("2023-01-01/2024-01-01") mismatch
// after
Interval taskInterval = getInterval();
TaskLock lock = taskActionClient.submit(new LockTryAcquireAction(new TaskLock("ds", taskInterval, "p")));
Defensive patterns

Strategy: validation

Validate before calling

if (!lock.getInterval().equals(task.getInterval())) { throw new IllegalStateException("lock/task interval mismatch, re-submit task"); }

Try / catch

try { lock = task.getAndCheckLock(taskActionClient); } catch (IllegalStateException e) { if (e.getMessage().contains("Lock interval")) { /* resubmit task / reacquire lock */ } else throw e; }

Prevention

When it happens

Trigger: A task subclass (or a mocked/stubbed TaskLock) returns a lock whose Interval differs from the task's configured interval; typically when task interval is set programmatically, after interval truncation/alignment, or when a lock is reused/misconfigured in tests or custom task types.

Common situations: Custom ingestion/kill/archive tasks built on AbstractFixedIntervalTask where the datasource/interval passed to the constructor doesn't match what the overlord granted; clock or timezone confusion causing the requested interval to shift; restoring tasks or spec edits that changed ioConfig.interval between submit and run.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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