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
- Call isJobExecutionResult() first and only call getJobExecutionResult() when it returns true.
- Use the concrete return type: if you submitted via execute() synchronously, the declared return is already JobExecutionResult.
- 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
- Always gate getJobExecutionResult() behind isJobExecutionResult().
- Use JobClient/RestClusterClient for async/detached job results.
- Type the variable as JobExecutionResult when you call execute() synchronously.
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
- Could not stop with a savepoint job "{}".
- Could not stop with a detached savepoint job "{}".
- Failed to trigger a savepoint for the job {}.
- JAR file is not a file: {}
- Missing JobId
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/8d48f9e40ecf9bb4.
Report an issue: GitHub.