flowable/flowable-engine · error · FlowableException

Job ${jobId} failed

Error message

Job ${jobId} failed

What it means

ManagementService.executeJob(String) wraps job execution (ExecuteJobCmd) and, when a non-Flowable RuntimeException escapes the command, rethrows it as a FlowableException with message "Job <jobId> failed" and the original as cause. FlowableExceptions themselves are rethrown unchanged. This normalizes unexpected runtime failures (NPE, IllegalStateException, etc.) during job execution.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/ManagementServiceImpl.java:153

    public String getTableName(Class<?> entityClass, boolean includePrefix) {
        return commandExecutor.execute(new GetTableNameCmd(entityClass, includePrefix));
    }

    @Override
    public TableMetaData getTableMetaData(String tableName) {
        return commandExecutor.execute(new GetTableMetaDataCmd(tableName, configuration.getEngineCfgKey()));
    }

    @Override
    public void executeJob(String jobId) {
        try {
            commandExecutor.execute(new ExecuteJobCmd(jobId, configuration.getJobServiceConfiguration()));

        } catch (RuntimeException e) {
            if (e instanceof FlowableException) {
                throw e;
            } else {
                throw new FlowableException("Job " + jobId + " failed", e);
            }
        }
    }
    
    @Override
    public void executeHistoryJob(String historyJobId) {
        commandExecutor.execute(new ExecuteHistoryJobCmd(historyJobId, configuration.getJobServiceConfiguration()));
    }

    @Override
    public String getHistoryJobHistoryJson(String historyJobId) {
        return commandExecutor.execute(new GetHistoryJobAdvancedConfigurationCmd(historyJobId, configuration.getJobServiceConfiguration()));
    }

    @Override
    public Job moveTimerToExecutableJob(String jobId) {
        return commandExecutor.execute(new MoveTimerToExecutableJobCmd(jobId, configuration.getJobServiceConfiguration()));
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Inspect the cause via e.getCause() — the real failure is the wrapped exception
  2. Fix the underlying job delegate/handler that threw the RuntimeException
  3. Redeploy the job's associated definition/classes so resolution succeeds
  4. Check the Flowable job log/exception stack for the original failure point

Example fix

// before
managementService.executeJob(jobId);
// after
try {
    managementService.executeJob(jobId);
} catch (FlowableException e) {
    Throwable cause = e.getCause();
    LOG.error("Job failed: {}", cause != null ? cause.getMessage() : e.getMessage(), cause != null ? cause : e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the job exists and inspect its last failure before re-executing
Job job = managementService.createJobQuery().jobId(jobId).singleResult();
if (job == null || job.getExceptionMessage() != null) {
    LOG.warn("Job {} previously failed: {}", jobId, job != null ? job.getExceptionMessage() : "not found");
}

Try / catch

try {
    managementService.executeJob(jobId);
} catch (FlowableException e) {
    Throwable root = e.getCause() != null ? e.getCause() : e;
    LOG.error("Job " + jobId + " failed due to " + root.getClass().getSimpleName(), root);
}

Prevention

When it happens

Trigger: Calling managementService.executeJob(jobId) where the job's handler throws a non-Flowable RuntimeException — e.g. a NullPointerException in a service task, class-cast errors in a delegate, or a broken job configuration.

Common situations: Manually re-executing a dead-letter/async job whose delegate class changed between deployments; jobs touching missing beans or classes causing RuntimeExceptions inside the command.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/2e841d1ececc7c61. Report an issue: GitHub.