apache/seatunnel · warning

GET /running-jobs dispatch delayed: dispatchDelayMs=

Error message

GET /running-jobs dispatch delayed: dispatchDelayMs={} thread={}

What it means

RunningJobsServlet.doGet logs this when GET /running-jobs waited over 500ms before dispatch, similar to the overview delay warning. The actual delay is also returned in the X-Dispatch-Delay-Ms response header.

Solutions

  1. Throttle/poll /running-jobs less frequently
  2. Increase REST server thread pool size
  3. Check coordinator node load (CPU, GC) and consider separating REST-serving and scheduling load
  4. Use pagination parameters (supported by this endpoint) to keep responses small
Defensive patterns

Strategy: retry

Validate before calling

const delay = Number(resp.headers.get('X-Dispatch-Delay-Ms') || 0);
if (delay > 500) await sleep(pollInterval);

Try / catch

try {
  const resp = await fetch(baseUrl + '/running-jobs?page=1&pageSize=20');
  if (Number(resp.headers.get('X-Dispatch-Delay-Ms')) > 500) {
    await sleep(backoff);
  }
} catch (e) { /* retry with backoff */ }

Prevention

When it happens

Trigger: GET /running-jobs when the REST thread pool is busy or the request is queued behind other work; heavy cluster load delaying servlet dispatch.

Common situations: High-frequency polling of /running-jobs by dashboards; REST thread pool exhaustion during cluster stress; coordinator node overloaded by scheduling work.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/636222ba040b94dd. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/servlet/RunningJobsServlet.java:81

                req.getRequestURI() != null
                        && req.getRequestURI().endsWith(RestConstant.REST_URL_RUNNING_JOBS_SUMMARY);
        boolean full = !summary || Boolean.parseBoolean(req.getParameter("full"));

        JsonArray runningJobs =
                full
                        ? jobInfoService.getRunningJobsJson()
                        : jobInfoService.getRunningJobsSummaryJson();

        long costMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
        long dispatchDelayMs = receivedMs <= 0 ? -1 : Math.max(0, nowMs - receivedMs);

        resp.setHeader("X-Dispatch-Delay-Ms", String.valueOf(dispatchDelayMs));
        resp.setHeader("X-Handler-Cost-Ms", String.valueOf(costMs));

        writeJsonWithPagination(req, resp, runningJobs);

        if (dispatchDelayMs > 500) {
            log.warn(
                    "GET /running-jobs dispatch delayed: dispatchDelayMs={} thread={}",
                    dispatchDelayMs,
                    Thread.currentThread().getName());
        }
        if (costMs > 500) {
            log.warn("GET /running-jobs slow: full={} costMs={}", full, costMs);
            Runtime rt = Runtime.getRuntime();
            long usedBytes = rt.totalMemory() - rt.freeMemory();
            log.warn(
                    "GET /running-jobs slow diagnostics: full={} costMs={} thread={} "
                            + "heapUsedMB={} heapTotalMB={} heapMaxMB={}",
                    full,
                    costMs,
                    Thread.currentThread().getName(),
                    usedBytes / 1024 / 1024,
                    rt.totalMemory() / 1024 / 1024,
                    rt.maxMemory() / 1024 / 1024);
        } else {

View on GitHub (pinned to cf67b549a7)