apache/flink · error · ClassCastException

This JobSubmissionResult is not a JobExecutionResult.

Error message

This JobSubmissionResult is not a JobExecutionResult.

What it means

Thrown by JobSubmissionResult.getJobExecutionResult() on the base class, which never holds an execution result. JobSubmissionResult represents a submitted job; only its subclass JobExecutionResult (returned by execute() on a local/mini-cluster environment) carries runtime accumulators and timing. Calling this on a plain JobSubmissionResult (e.g., from detached submission) is a type mismatch the API refuses statically by always throwing ClassCastException.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/common/JobSubmissionResult.java:60

    /**
     * Checks if this JobSubmissionResult is also a JobExecutionResult. See {@code
     * getJobExecutionResult} to retrieve the JobExecutionResult.
     *
     * @return True if this is a JobExecutionResult, false otherwise
     */
    public boolean isJobExecutionResult() {
        return false;
    }

    /**
     * Returns the JobExecutionResult if available.
     *
     * @return The JobExecutionResult
     * @throws ClassCastException if this is not a JobExecutionResult
     */
    public JobExecutionResult getJobExecutionResult() {
        throw new ClassCastException("This JobSubmissionResult is not a JobExecutionResult.");
    }
}

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Call isJobExecutionResult() first and only call getJobExecutionResult() when it returns true.
  2. Use the concrete return type: if you submitted via execute() synchronously, the declared return is already JobExecutionResult.
  3. For detached/async jobs, fetch results later via the RestClusterClient/JobClient instead of the submission result.

Example fix

// before
JobExecutionResult r = result.getJobExecutionResult();
// after
if (result.isJobExecutionResult()) {
    JobExecutionResult r = result.getJobExecutionResult();
    // use r.getJobExecutionTime(), accumulators, etc.
} else {
    // detached/async: poll via JobClient instead
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (result.isJobExecutionResult()) {
    JobExecutionResult r = result.getJobExecutionResult();
}

Type guard

public static Optional<JobExecutionResult> asExecutionResult(JobSubmissionResult r) {
    return r != null && r.isJobExecutionResult()
        ? Optional.of(r.getJobExecutionResult())
        : Optional.empty();
}

Prevention

When it happens

Trigger: Calling result.getJobExecutionResult() without first checking isJobExecutionResult(); storing a JobSubmissionResult reference and downcasting blindly; using execute() vs executeAsync()/detach mode interchangeably.

Common situations: Detached submission (env.executeAsync / detached mode) returns a bare JobSubmissionResult; reusing code that expected the synchronous execute() return type; refactoring from blocking to async submission.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/8d48f9e40ecf9bb4. Report an issue: GitHub.