{"record":{"id":"a5c793e7209f2ab4","repo":"flowable/flowable-engine","slug":"job-jobid-parent-is-not-process-instance","errorCode":null,"errorMessage":"Job <jobId> parent is not process instance","messagePattern":"Job <jobId> parent is not process instance","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/DefaultProcessJobParentStateResolver.java","lineNumber":35,"sourceCode":"import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;\nimport org.flowable.engine.runtime.ProcessInstance;\nimport org.flowable.job.api.Job;\nimport org.flowable.job.service.InternalJobParentStateResolver;\n\n/**\n * @author martin.grofcik\n */\npublic class DefaultProcessJobParentStateResolver implements InternalJobParentStateResolver {\n    protected ProcessEngineConfigurationImpl processEngineConfiguration;\n\n    public DefaultProcessJobParentStateResolver(ProcessEngineConfigurationImpl processEngineConfiguration) {\n        this.processEngineConfiguration = processEngineConfiguration;\n    }\n\n    @Override\n    public boolean isSuspended(Job job) {\n        if (StringUtils.isEmpty(job.getProcessInstanceId())) {\n            throw new FlowableIllegalArgumentException(\"Job \" + job.getId() + \" parent is not process instance\");\n        }\n        ProcessInstance processInstance = this.processEngineConfiguration.getRuntimeService().createProcessInstanceQuery().processInstanceId(job.getProcessInstanceId()).singleResult();\n        return processInstance.isSuspended();\n    }\n}\n","sourceCodeStart":17,"sourceCodeEnd":41,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/DefaultProcessJobParentStateResolver.java#L17-L41","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the job actually belongs to a process instance before invoking suspension logic on it.","Check StringUtils.isEmpty(job.getProcessInstanceId()) in your own code first and use a job-specific suspension path for standalone jobs.","If the job should have a process instance, investigate why the parent is missing (corrupt/migrated ACT_RU_JOB rows)."],"exampleFix":"// before\nboolean suspended = jobParentStateResolver.isSuspended(job); // throws for standalone jobs\n// after\nif (StringUtils.isNotEmpty(job.getProcessInstanceId())) {\n    suspended = jobParentStateResolver.isSuspended(job);\n} else {\n    suspended = managementService.isJobSuspended(job.getId());\n}","handlingStrategy":"validation","validationCode":"if (job == null || StringUtils.isEmpty(job.getProcessInstanceId())) {\n    throw new IllegalArgumentException(\"Job has no owning process instance; use job-level suspension API\");\n}","typeGuard":"boolean isProcessScopedJob(Job job) { return job != null && StringUtils.isNotEmpty(job.getProcessInstanceId()); }","tryCatchPattern":"try {\n    suspended = resolver.isSuspended(job);\n} catch (FlowableIllegalArgumentException e) {\n    suspended = false; // or fall back to job-level state lookup\n    log.warn(\"Job {} has no process instance parent\", job.getId());\n}","preventionTips":["Verify a job's processInstanceId before using process-instance parent resolvers","Handle standalone/async jobs with dedicated job management APIs","Check ACT_RU_JOB.PROCESS_INSTANCE_ID_ data integrity after migrations"],"tags":["flowable","job","process-instance","suspension-state"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}