{"record":{"id":"5736bc307847dfe1","repo":"flowable/flowable-engine","slug":"lockduration-is-null","errorCode":null,"errorMessage":"lockDuration is null","messagePattern":"lockDuration is null","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/ExternalWorkerJobAcquireBuilderImpl.java","lineNumber":56,"sourceCode":"    protected Duration lockDuration;\n    protected String scopeType;\n    protected String tenantId;\n    protected String authorizedUser;\n    protected Collection<String> authorizedGroups;\n\n    public ExternalWorkerJobAcquireBuilderImpl(CommandExecutor commandExecutor, JobServiceConfiguration jobServiceConfiguration) {\n        this.commandExecutor = commandExecutor;\n        this.jobServiceConfiguration = jobServiceConfiguration;\n    }\n\n    @Override\n    public ExternalWorkerJobAcquireBuilder topic(String topic, Duration lockDuration) {\n        if (topic == null) {\n            throw new FlowableIllegalArgumentException(\"topic is null\");\n        }\n\n        if (lockDuration == null) {\n            throw new FlowableIllegalArgumentException(\"lockDuration is null\");\n        }\n\n        this.topic = topic;\n        this.lockDuration = lockDuration;\n        return this;\n    }\n\n    @Override\n    public ExternalWorkerJobAcquireBuilder onlyBpmn() {\n        if (ScopeTypes.CMMN.equals(scopeType)) {\n            throw new FlowableIllegalArgumentException(\"Cannot combine onlyCmmn() with onlyBpmn() in the same query\");\n        }\n\n        if (scopeType != null) {\n            throw new FlowableIllegalArgumentException(\"Cannot combine scopeType(String) with onlyBpmn() in the same query\");\n        }\n        return scopeType(ScopeTypes.BPMN);\n    }","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/ExternalWorkerJobAcquireBuilderImpl.java#L38-L74","documentation":"ExternalWorkerJobAcquireBuilderImpl.topic() requires a non-null lockDuration so the engine knows how long to lock acquired jobs. A null Duration makes lock bookkeeping impossible, so Flowable throws FlowableIllegalArgumentException.","triggerScenarios":"Calling builder.topic(\"myTopic\", null), e.g. when lock duration is read from config that returns null or a missing Duration parse.","commonSituations":"Misconfigured lock duration property; using Duration.ofMillis of an unset numeric config; refactor dropping a default duration.","solutions":["Pass an explicit Duration, e.g. Duration.ofMinutes(1)","Provide a default lock duration when the configured one is null","Validate config values at startup"],"exampleFix":"// before\nbuilder.topic(\"orderTopic\", parseDuration(cfg));\n// after\nbuilder.topic(\"orderTopic\", Optional.ofNullable(parseDuration(cfg)).orElse(Duration.ofMinutes(1)));","handlingStrategy":"validation","validationCode":"if (lockDuration == null || lockDuration.isZero() || lockDuration.isNegative()) throw new IllegalArgumentException(\"invalid lockDuration\");","typeGuard":"boolean validLockDuration(Duration d) { return d != null && !d.isZero() && !d.isNegative(); }","tryCatchPattern":"try { builder.topic(topic, lockDuration); } catch (FlowableIllegalArgumentException e) { if (!\"lockDuration is null\".equals(e.getMessage())) throw e; /* fall back to default duration */ }","preventionTips":["Define a default lock duration constant","Parse durations from config with explicit error handling","Validate duration config at startup"],"tags":["flowable","external-worker","null-argument","validation"],"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"}