apache/seatunnel · error · RuntimeException

Unsafe invoke, the current thread[%s] has not acquired the l

Error message

Unsafe invoke, the current thread[%s] has not acquired the lock[%s].

What it means

SplitFetcher methods suffixed 'Unsafe' (getNextTaskUnsafe) must only be called while holding the fetcher's ReentrantLock. getNextTaskUnsafe explicitly checks lock.isHeldByCurrentThread() and throws if the calling thread does not own the lock, guarding internal task-queue state from unsynchronized access.

Source

Thrown at seatunnel-connectors-v2/connector-common/src/main/java/org/apache/seatunnel/connectors/seatunnel/common/source/reader/fetcher/SplitFetcher.java:183

                    String.format(
                            "SplitFetcher thread %d received unexpected exception while polling the records",
                            fetcherId),
                    e);
        }

        // re-acquire lock as all post-processing steps, need it
        lock.lock();
        try {
            this.runningTask = null;
        } finally {
            lock.unlock();
        }
        return true;
    }

    private SplitFetcherTask getNextTaskUnsafe() {
        if (!lock.isHeldByCurrentThread()) {
            throw new RuntimeException(
                    String.format(
                            "Unsafe invoke, the current thread[%s] has not acquired the lock[%s].",
                            Thread.currentThread().getName(), this.lock.toString()));
        }

        try {
            if (!taskQueue.isEmpty()) {
                // execute tasks in taskQueue first
                return taskQueue.poll();
            } else if (!assignedSplits.isEmpty()) {
                // use fallback task = fetch if there is at least one split
                return fetchTask;
            } else {
                // nothing to do, wait for signal
                nonEmpty.await();
                return taskQueue.poll();
            }
        } catch (InterruptedException e) {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Acquire the lock before calling: lock.lock() then try { getNextTaskUnsafe(); } finally { lock.unlock(); }
  2. Don't call Unsafe methods from outside SplitFetcher.run() — use public APIs (addTask, shutdown, wakeUp) which handle locking
  3. If subclassing SplitFetcher, mirror the original run() locking structure
  4. Restore the original runOnce()/run() call pattern if a refactor removed lock acquisition

Example fix

// before
splitFetcher.getNextTaskUnsafe(); // wrong thread, no lock
// after
splitFetcher.lock.lock();
try {
    splitFetcher.getNextTaskUnsafe();
} finally {
    splitFetcher.lock.unlock();
}
Defensive patterns

Strategy: type-guard

Validate before calling

// assert ownership before unsafe calls
if (!splitFetcher.lock.isHeldByCurrentThread()) {
    throw new AssertionError("must hold SplitFetcher lock before unsafe calls");
}

Type guard

boolean canCallUnsafe(SplitFetcher f) {
    return f.lock.isHeldByCurrentThread();
}

Prevention

When it happens

Trigger: Any code path calling getNextTaskUnsafe (directly or via runOnce refactoring) from a thread that has not acquired SplitFetcher's lock — typically new custom code or subclasses touching fetcher internals, since run() normally acquires the lock before runOnce().

Common situations: Custom SplitFetcher subclass overriding run()/runOnce() without wrapping calls in lock.lock()/unlock(); external tooling invoking private-adjacent unsafe methods; concurrency refactors that dropped lock acquisition.

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/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/edcaeaf61e6422d8. Report an issue: GitHub.