{"record":{"id":"e2e5f766f74b2be2","repo":"flowable/flowable-engine","slug":"job-is-null-e2e5f7","errorCode":null,"errorMessage":"job is null","messagePattern":"job is null","errorType":"exception","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cmd/UnlockExclusiveJobCmd.java","lineNumber":45,"sourceCode":" * @author Joram Barrez\n */\npublic class UnlockExclusiveJobCmd implements Command<Object>, Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(UnlockExclusiveJobCmd.class);\n\n    protected JobEntity job;\n\n    public UnlockExclusiveJobCmd(JobEntity job) {\n        this.job = job;\n    }\n\n    @Override\n    public Object execute(CommandContext commandContext) {\n\n        if (job == null) {\n            throw new ActivitiIllegalArgumentException(\"job is null\");\n        }\n\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"Unlocking exclusive job {}\", job.getId());\n        }\n\n        if (job.isExclusive()) {\n            if (job.getProcessInstanceId() != null) {\n                ExecutionEntity execution = commandContext.getExecutionEntityManager().findExecutionById(job.getProcessInstanceId());\n                if (execution != null) {\n                    commandContext.getExecutionEntityManager().clearProcessInstanceLockTime(execution.getId());\n                }\n            }\n        }\n\n        return null;\n    }\n}","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cmd/UnlockExclusiveJobCmd.java#L27-L63","documentation":"Thrown by UnlockExclusiveJobCmd.execute() as ActivitiIllegalArgumentException when the command is constructed with a null job reference. The command exists to release the exclusive-lock held by an exclusive job, and without a job object it has nothing to unlock, so it fails fast.","triggerScenarios":"Internally constructed UnlockExclusiveJobCmd(null) — typically from job executor/acquisition code paths where the job entity failed to load or was already removed before the unlock command is dispatched; direct command use passing a null JobEntity.","commonSituations":"Race conditions where the job was deleted between acquisition and unlock; custom job executor extensions or tests invoking the command directly; misconfigured job manager wiring passing an unset job variable.","solutions":["Ensure the job is loaded from the DB (managementService.createJobQuery().jobId(id).singleResult()) before constructing the unlock command; skip unlocking if it no longer exists.","If this surfaces from the default job executor, upgrade the engine — this indicates an internal race fixed in later versions.","In custom code, guard the argument: only create UnlockExclusiveJobCmd when the JobEntity is non-null.","Check for concurrent job deletion (timeout handlers deleting a job while it executes) and serialize those operations."],"exampleFix":"// before\ncommandExecutor.execute(new UnlockExclusiveJobCmd(job)); // job may be null\n// after\nif (job != null) {\n    commandExecutor.execute(new UnlockExclusiveJobCmd(job));\n}","handlingStrategy":"type-guard","validationCode":"Job job = managementService.createJobQuery().jobId(jobId).singleResult();\nif (job == null) {\n    return; // already deleted; nothing to unlock\n}","typeGuard":"boolean isUnlockable(Job job) {\n    return job != null && job.getId() != null;\n}","tryCatchPattern":"try {\n    commandExecutor.execute(new UnlockExclusiveJobCmd(job));\n} catch (ActivitiIllegalArgumentException e) {\n    if (\"job is null\".equals(e.getMessage())) {\n        log.warn(\"Skipped unlock: job reference was null\");\n    } else {\n        throw e;\n    }\n}","preventionTips":["Check job existence before constructing unlock commands","Avoid manual UnlockExclusiveJobCmd usage unless implementing custom job execution","Keep the engine version consistent across modules to avoid internal races","Log job lifecycle (acquire/delete/unlock) when debugging job executor races"],"tags":["null-argument","job-executor","exclusive-job","locking"],"backgroundTag":"null-argument","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}