flowable/flowable-engine · error · FlowableIllegalArgumentException

Converter can only convert localDateTime

Error message

Converter can only convert localDateTime

What it means

LocalDateTimeRestVariableConverter.convertVariableValue serializes engine variables to REST and requires the value to be a java.time.LocalDateTime. Any other non-null object fails the instanceof check and throws FlowableIllegalArgumentException, as only LocalDateTime can be written as a localDateTime REST variable.

Source

Thrown at modules/flowable-common-rest/src/main/java/org/flowable/common/rest/variable/LocalDateTimeRestVariableConverter.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 localDateTime");
            }
            try {
                return LocalDateTime.parse((String) result.getValue());
            } catch (DateTimeParseException e) {
                throw new FlowableIllegalArgumentException("The given variable value is not a localDateTime: '" + result.getValue() + "'", e);
            }
        }
        return null;
    }

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

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Convert the value to LocalDateTime (e.g. LocalDateTime.ofInstant(instant, zoneId)) before serialization.
  2. Match the REST variable type to the actual Java type (use the appropriate converter for Date/ZonedDateTime).
  3. Parse Strings with LocalDateTime.parse before conversion.
  4. Migrate persisted legacy datetime variables to LocalDateTime.

Example fix

// before
converter.convertVariableValue(Instant.now(), restVar); // throws

// after
LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
converter.convertVariableValue(ldt, restVar);
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

static boolean isLocalDateTime(Object v) {
    return v instanceof java.time.LocalDateTime;
}

Try / catch

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

Prevention

When it happens

Trigger: Calling convertVariableValue(value, result) with a non-null object that is not LocalDateTime (e.g. java.util.Date, ZonedDateTime, OffsetDateTime, Instant, String), typically when the engine variable's type is exposed as localDateTime via REST.

Common situations: Legacy java.util.Date variables surfaced as localDateTime; zoned types (ZonedDateTime/Instant) from calendars or external systems; String values never parsed into LocalDateTime.

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/d158a56b12a7177c. Report an issue: GitHub.