flowable/flowable-engine · error · FlowableIllegalArgumentException

lockDuration is null

Error message

lockDuration is null

What it means

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.

Solutions

  1. Pass an explicit Duration, e.g. Duration.ofMinutes(1)
  2. Provide a default lock duration when the configured one is null
  3. Validate config values at startup

Example fix

// before
builder.topic("orderTopic", parseDuration(cfg));
// after
builder.topic("orderTopic", Optional.ofNullable(parseDuration(cfg)).orElse(Duration.ofMinutes(1)));
Defensive patterns

Strategy: validation

Validate before calling

if (lockDuration == null || lockDuration.isZero() || lockDuration.isNegative()) throw new IllegalArgumentException("invalid lockDuration");

Type guard

boolean validLockDuration(Duration d) { return d != null && !d.isZero() && !d.isNegative(); }

Try / catch

try { builder.topic(topic, lockDuration); } catch (FlowableIllegalArgumentException e) { if (!"lockDuration is null".equals(e.getMessage())) throw e; /* fall back to default duration */ }

Prevention

When it happens

Trigger: Calling builder.topic("myTopic", null), e.g. when lock duration is read from config that returns null or a missing Duration parse.

Common situations: Misconfigured lock duration property; using Duration.ofMillis of an unset numeric config; refactor dropping a default duration.

Related errors


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

Appendix: source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/ExternalWorkerJobAcquireBuilderImpl.java:56

    protected Duration lockDuration;
    protected String scopeType;
    protected String tenantId;
    protected String authorizedUser;
    protected Collection<String> authorizedGroups;

    public ExternalWorkerJobAcquireBuilderImpl(CommandExecutor commandExecutor, JobServiceConfiguration jobServiceConfiguration) {
        this.commandExecutor = commandExecutor;
        this.jobServiceConfiguration = jobServiceConfiguration;
    }

    @Override
    public ExternalWorkerJobAcquireBuilder topic(String topic, Duration lockDuration) {
        if (topic == null) {
            throw new FlowableIllegalArgumentException("topic is null");
        }

        if (lockDuration == null) {
            throw new FlowableIllegalArgumentException("lockDuration is null");
        }

        this.topic = topic;
        this.lockDuration = lockDuration;
        return this;
    }

    @Override
    public ExternalWorkerJobAcquireBuilder onlyBpmn() {
        if (ScopeTypes.CMMN.equals(scopeType)) {
            throw new FlowableIllegalArgumentException("Cannot combine onlyCmmn() with onlyBpmn() in the same query");
        }

        if (scopeType != null) {
            throw new FlowableIllegalArgumentException("Cannot combine scopeType(String) with onlyBpmn() in the same query");
        }
        return scopeType(ScopeTypes.BPMN);
    }

View on GitHub (pinned to d6d39ce1c6)