flowable/flowable-engine · error · FlowableIllegalArgumentException

Job <jobId> parent is not process instance

Error message

Job <jobId> parent is not process instance

What it means

DefaultProcessJobParentStateResolver.isSuspended(Job) only supports jobs whose parent is a process instance. If the job's processInstanceId is empty (e.g. a standalone/async-executor job with no owning process instance) it throws FlowableIllegalArgumentException, because the resolver would otherwise dereference a null ProcessInstance. It is a guard that this parent-state resolver is only used for process-scoped jobs.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/DefaultProcessJobParentStateResolver.java:35

import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.job.api.Job;
import org.flowable.job.service.InternalJobParentStateResolver;

/**
 * @author martin.grofcik
 */
public class DefaultProcessJobParentStateResolver implements InternalJobParentStateResolver {
    protected ProcessEngineConfigurationImpl processEngineConfiguration;

    public DefaultProcessJobParentStateResolver(ProcessEngineConfigurationImpl processEngineConfiguration) {
        this.processEngineConfiguration = processEngineConfiguration;
    }

    @Override
    public boolean isSuspended(Job job) {
        if (StringUtils.isEmpty(job.getProcessInstanceId())) {
            throw new FlowableIllegalArgumentException("Job " + job.getId() + " parent is not process instance");
        }
        ProcessInstance processInstance = this.processEngineConfiguration.getRuntimeService().createProcessInstanceQuery().processInstanceId(job.getProcessInstanceId()).singleResult();
        return processInstance.isSuspended();
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure the job actually belongs to a process instance before invoking suspension logic on it.
  2. Check StringUtils.isEmpty(job.getProcessInstanceId()) in your own code first and use a job-specific suspension path for standalone jobs.
  3. If the job should have a process instance, investigate why the parent is missing (corrupt/migrated ACT_RU_JOB rows).

Example fix

// before
boolean suspended = jobParentStateResolver.isSuspended(job); // throws for standalone jobs
// after
if (StringUtils.isNotEmpty(job.getProcessInstanceId())) {
    suspended = jobParentStateResolver.isSuspended(job);
} else {
    suspended = managementService.isJobSuspended(job.getId());
}
Defensive patterns

Strategy: validation

Validate before calling

if (job == null || StringUtils.isEmpty(job.getProcessInstanceId())) {
    throw new IllegalArgumentException("Job has no owning process instance; use job-level suspension API");
}

Type guard

boolean isProcessScopedJob(Job job) { return job != null && StringUtils.isNotEmpty(job.getProcessInstanceId()); }

Try / catch

try {
    suspended = resolver.isSuspended(job);
} catch (FlowableIllegalArgumentException e) {
    suspended = false; // or fall back to job-level state lookup
    log.warn("Job {} has no process instance parent", job.getId());
}

Prevention

When it happens

Trigger: Suspending/resuming or checking suspension state of a Job whose getProcessInstanceId() returns null or empty — e.g. a timer or async job created outside a process instance, or management API calls on such jobs routed through this resolver.

Common situations: Calling ManagementService or JobService suspension APIs on standalone jobs (external worker/async history jobs) after upgrading Flowable versions where job parenting changed; data migrated from another engine where PROCESS_INSTANCE_ID_ is null.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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