quarkusio/quarkus · error · IllegalStateException

A skipPredicate instance may not be serialized

Error message

A skipPredicate instance may not be serialized

What it means

SyntheticScheduled is the serializable description of a programmatically created @Scheduled method; toJson() flattens it to JSON for the build-time/CDI synthetic bean machinery. A skipPredicate configured as a live object instance cannot be represented in JSON, so toJson() throws this IllegalStateException if one is present.

Source

Thrown at extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SyntheticScheduled.java:114

    @Override
    public String executeWith() {
        return implementation;
    }

    @Override
    public String executionMaxDelay() {
        return executionMaxDelay;
    }

    @Override
    public String description() {
        return description;
    }

    public String toJson() {
        if (skipPredicate != null) {
            throw new IllegalStateException("A skipPredicate instance may not be serialized");
        }
        JsonObject json = new JsonObject();
        json.put("identity", identity);
        json.put("cron", cron);
        json.put("every", every);
        json.put("delay", delay);
        json.put("delayUnit", delayUnit.toString());
        json.put("delayed", delayed);
        json.put("overdueGracePeriod", overdueGracePeriod);
        json.put("concurrentExecution", concurrentExecution.toString());
        json.put("timeZone", timeZone);
        json.put("executeWith", implementation);
        json.put("executionMaxDelay", executionMaxDelay);
        json.put("description", description);
        return json.encode();
    }

    public static SyntheticScheduled fromJson(String json) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Configure the skip predicate as a Class (setSkipPredicate(Class)) so the JSON can reference it by name instead of an instance
  2. Avoid serializing SyntheticScheduled with instance-based predicates; register the predicate as a CDI bean and reference its class
  3. If persistence of job metadata is required, ensure predicates are class-referenced before toJson() is invoked

Example fix

// before
jobDef.setSkipPredicate(new MySkipPredicate()); // instance -> not serializable
// after
jobDef.setSkipPredicate(MySkipPredicate.class);
Defensive patterns

Strategy: validation

Validate before calling

if (synthetic != null && syntheticHasInstanceSkipPredicate(synthetic)) {
    throw new IllegalStateException("use setSkipPredicate(Class) for serializable schedules");
}

Type guard

boolean serializableSchedule(SyntheticScheduled s) {
    return s.skipPredicateClass() != null; // class-referenced predicate, not an instance
}

Try / catch

try {
    json = synthetic.toJson();
} catch (IllegalStateException e) {
    LOG.error("Schedule uses an instance skipPredicate; switch to a Class reference", e);
}

Prevention

When it happens

Trigger: Serializing a SyntheticScheduled whose skipPredicate field is a non-null instance, i.e. a job definition built with setSkipPredicate(instance) rather than a class name, then having its metadata converted to JSON (e.g. recorded synthetic bean output).

Common situations: Using a Lambda or prebuilt SkipPredicate object in a programmatically scheduled job and then serializing the schedule (Quartz job-store persistence or build-time recording); moving from skip predicate classes to instances across versions.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/6dc270a02884f9a5. Report an issue: GitHub.