flowable/flowable-engine · error · FlowableIllegalArgumentException

Converter can only convert localDate

Error message

Converter can only convert localDate

What it means

LocalDateRestVariableConverter.convertVariableValue serializes an engine variable into a REST variable and requires the underlying value to be a java.time.LocalDate. Any other non-null object fails the instanceof check and throws FlowableIllegalArgumentException, since only LocalDate values can be rendered as localDate REST variables.

Source

Thrown at modules/flowable-common-rest/src/main/java/org/flowable/common/rest/variable/LocalDateRestVariableConverter.java:54

    public Object getVariableValue(EngineRestVariable result) {
        if (result.getValue() != null) {
            if (!(result.getValue() instanceof String)) {
                throw new FlowableIllegalArgumentException("Converter can only convert string to localDate");
            }
            try {
                return LocalDate.parse((String) result.getValue());
            } catch (DateTimeParseException e) {
                throw new FlowableIllegalArgumentException("The given variable value is not a localDate: '" + result.getValue() + "'", e);
            }
        }
        return null;
    }

    @Override
    public void convertVariableValue(Object variableValue, EngineRestVariable result) {
        if (variableValue != null) {
            if (!(variableValue instanceof LocalDate)) {
                throw new FlowableIllegalArgumentException("Converter can only convert localDate");
            }
            result.setValue(variableValue.toString());
        } else {
            result.setValue(null);
        }
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Convert the value to java.time.LocalDate (e.g. via Instant.atZone(...).toLocalDate() for Date) before serialization.
  2. Declare/return the correct REST variable type for the actual Java type (e.g. use the Date converter for java.util.Date).
  3. Migrate persisted variables of legacy types to LocalDate where possible.
  4. Parse String values with LocalDate.parse before handing them to the converter.

Example fix

// before
converter.convertVariableValue(new java.util.Date(), restVar); // throws

// after
LocalDate d = new java.util.Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
converter.convertVariableValue(d, restVar);
Defensive patterns

Strategy: type-guard

Validate before calling

if (value instanceof LocalDate) {
    converter.convertVariableValue(value, restVar);
} else if (value instanceof java.util.Date d) {
    converter.convertVariableValue(LocalDate.ofInstant(d.toInstant(), ZoneId.systemDefault()), restVar);
} else {
    throw new IllegalArgumentException("Cannot serialize as localDate: " + value.getClass());
}

Type guard

static boolean isLocalDate(Object v) {
    return v instanceof java.time.LocalDate;
}

Try / catch

try {
    converter.convertVariableValue(value, restVar);
} catch (FlowableIllegalArgumentException e) {
    throw new IllegalStateException("localDate REST variable requires java.time.LocalDate, got: "
        + value.getClass().getName(), e);
}

Prevention

When it happens

Trigger: Calling convertVariableValue(value, result) with a non-null object that is not a LocalDate (e.g. java.util.Date, String, LocalDateTime, ZonedDateTime), typically when serializing process/engine variables for REST responses where the variable type was declared localDate.

Common situations: Storing legacy java.util.Date variables but labeling them localDate in REST; a migration from Date to java.time leaving old variable instances; passing Strings that were never parsed into LocalDate.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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