flowable/flowable-engine · error · FlowableIllegalArgumentException

Cannot combine onlyTimers() with onlyMessages() in the same

Error message

Cannot combine onlyTimers() with onlyMessages() in the same query

What it means

DeadLetterJobQueryImpl.timers() throws FlowableIllegalArgumentException when onlyMessages is already set on the query. onlyTimers(), onlyMessages(), and onlyExternalWorkers() are mutually exclusive job-type filters; combining them would produce contradictory criteria, so the second call fails fast. The message text mentions onlyMessages because that flag was set first.

Source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/DeadLetterJobQueryImpl.java:429

            this.handlerTypes = handlerTypes;
        }
        return this;
    }

    @Override
    public DeadLetterJobQueryImpl executable() {
        if (inOrStatement) {
            this.currentOrQueryObject.executable = true;
        } else {
            this.executable = true;
        }
        return this;
    }

    @Override
    public DeadLetterJobQueryImpl timers() {
        if (onlyMessages) {
            throw new FlowableIllegalArgumentException("Cannot combine onlyTimers() with onlyMessages() in the same query");
        }

        if (onlyExternalWorkers) {
            throw new FlowableIllegalArgumentException("Cannot combine onlyExternalWorkers() with onlyMessages() in the same query");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.onlyTimers = true;
        } else {
            this.onlyTimers = true;
        }
        return this;
    }

    @Override
    public DeadLetterJobQueryImpl messages() {
        if (onlyTimers) {
            throw new FlowableIllegalArgumentException("Cannot combine onlyTimers() with onlyMessages() in the same query");
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Pick exactly one job-type filter per query: only onlyTimers(), onlyMessages(), or onlyExternalWorkers().
  2. If multiple types are needed, run separate queries and merge the results in application code.
  3. Always start from a fresh managementService.createDeadLetterJobQuery() instead of reusing a configured query object.

Example fix

// before
DeadLetterJobQuery q = mgmt.createDeadLetterJobQuery().onlyMessages();
if (includeTimers) { q.timers(); } // throws
// after
DeadLetterJobQuery q = mgmt.createDeadLetterJobQuery();
if (includeTimers) {
    q = mgmt.createDeadLetterJobQuery().timers();
} else {
    q = mgmt.createDeadLetterJobQuery().onlyMessages();
}
Defensive patterns

Strategy: validation

Validate before calling

int typeFilters = (useTimers ? 1 : 0) + (useMessages ? 1 : 0) + (useExternalWorkers ? 1 : 0);
if (typeFilters > 1) { throw new IllegalArgumentException("Select at most one of timers/messages/externalWorkers"); }

Type guard

boolean canApplyTypeFilter(DeadLetterJobQueryImpl q) { return !q.onlyMessages && !q.onlyExternalWorkers; }

Try / catch

try { query.timers(); } catch (FlowableIllegalArgumentException e) { log.warn("Conflicting job type filters: {}", e.getMessage()); query = managementService.createDeadLetterJobQuery(); }

Prevention

When it happens

Trigger: Calling deadLetterJobQuery().onlyMessages()...timers() (or vice versa) on the same query object; also triggers when onlyExternalWorkers was set and timers() is called (with its own companion message). Reusing a cached/shared query instance that already carries one of these flags.

Common situations: Building query filters dynamically from user-selected checkboxes that allow multiple type selections; appending filters to a shared query template; merging two queries' criteria into one object.

Related errors


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