{"record":{"id":"cf4e34dc72ec9a04","repo":"flowable/flowable-engine","slug":"topic-must-not-be-empty","errorCode":null,"errorMessage":"topic must not be empty","messagePattern":"topic must not be empty","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AcquireExternalWorkerJobsCmd.java","lineNumber":57,"sourceCode":"    protected final String workerId;\n    protected final int numberOfJobs;\n    protected final ExternalWorkerJobAcquireBuilderImpl builder;\n    protected final JobServiceConfiguration jobServiceConfiguration;\n\n    public AcquireExternalWorkerJobsCmd(String workerId, int numberOfJobs, ExternalWorkerJobAcquireBuilderImpl builder,\n            JobServiceConfiguration jobServiceConfiguration) {\n        \n        this.workerId = workerId;\n        this.numberOfJobs = numberOfJobs;\n        this.builder = builder;\n        this.jobServiceConfiguration = jobServiceConfiguration;\n    }\n\n    @Override\n    public List<AcquiredExternalWorkerJob> execute(CommandContext commandContext) {\n        String topic = builder.getTopic();\n        if (StringUtils.isEmpty(topic)) {\n            throw new FlowableIllegalArgumentException(\"topic must not be empty\");\n        }\n\n        if (numberOfJobs < 1) {\n            throw new FlowableIllegalArgumentException(\"requested number of jobs must not be smaller than 1\");\n        }\n\n        if (StringUtils.isEmpty(workerId)) {\n            throw new FlowableIllegalArgumentException(\"workerId must not be empty\");\n        }\n\n        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();\n        InternalJobManager internalJobManager = jobServiceConfiguration.getInternalJobManager();\n\n        List<ExternalWorkerJobEntity> jobs = externalWorkerJobEntityManager.findExternalJobsToExecute(builder, numberOfJobs);\n\n        int lockTimeInMillis = (int) builder.getLockDuration().abs().toMillis();\n        List<AcquiredExternalWorkerJob> acquiredJobs = new ArrayList<>(jobs.size());\n","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AcquireExternalWorkerJobsCmd.java#L39-L75","documentation":"Thrown by AcquireExternalWorkerJobsCmd.execute() when the topic on the ExternalWorkerJobAcquireBuilder is null or empty. The topic is the category workers subscribe to, and the acquisition query filters jobs by it; without it Flowable cannot build a meaningful acquisition and fails fast.","triggerScenarios":"managementService.createExternalWorkerJobAcquireBuilder().acquireAndLock(...) without topic(...); passing a null/empty topic from configuration; building the acquire request programmatically from user input where topic was optional.","commonSituations":"Misconfigured worker deployment where the topic property is missing; a generic dispatcher that forgot to set the topic; renaming topics across versions and leaving the old key unset.","solutions":["Call topic(\"yourTopic\") on the ExternalWorkerJobAcquireBuilder before acquiring","Externalize the topic into worker configuration and validate it at startup (fail fast if blank)","Match the topic exactly to the one set on the business job (jobServiceConfig setting externalWorkerJobTopic / setJobTopic)"],"exampleFix":"// before\nExternalWorkerJobAcquireBuilder builder = managementService.createExternalWorkerJobAcquireBuilder();\nList<AcquiredExternalWorkerJob> jobs = builder.acquireAndLock(numberOfJobs, workerId, lockDuration);\n// after\nList<AcquiredExternalWorkerJob> jobs = managementService.createExternalWorkerJobAcquireBuilder()\n    .topic(\"myTopic\")\n    .acquireAndLock(numberOfJobs, workerId, lockDuration);","handlingStrategy":"validation","validationCode":"if (topic == null || topic.isEmpty()) {\n    throw new IllegalArgumentException(\"topic must be configured before acquiring jobs\");\n}","typeGuard":"boolean hasTopic(String t) { return t != null && !t.trim().isEmpty(); }","tryCatchPattern":"try {\n    acquireBuilder.topic(topic).acquireAndLock(n, workerId, lockDuration);\n} catch (FlowableIllegalArgumentException e) {\n    LOGGER.error(\"Acquire rejected: {}\", e.getMessage());\n}","preventionTips":["Validate topic configuration at worker startup","Keep topic values in a shared constant used by both job creation and acquisition","Reject blank topics at the config-loading layer"],"tags":["validation","flowable","acquire"],"backgroundTag":"empty-required-field","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"}