flowable/flowable-engine · error · ActivitiIllegalArgumentException

null value cannot be saved

Error message

null value cannot be saved

What it means

JPAEntityMappings serializes JPA entity variables for persistence. getJPAClassString extracts the entity class name for storage; it refuses a null value because a null cannot carry JPA metadata and cannot be persisted as a JPA entity variable.

Solutions

  1. Ensure the value is a non-null JPA entity before setting the variable
  2. Guard the caller: if the entity is null, set the variable to null explicitly via a null-typed variable instead of the JPA mapping, or remove the variable
  3. Fix the upstream fetch so the entity is loaded before it reaches variable serialization

Example fix

// before
runtimeService.setVariable(executionId, "order", possiblyNullOrder);
// after
if (possiblyNullOrder != null) {
    runtimeService.setVariable(executionId, "order", possiblyNullOrder);
} else {
    runtimeService.setVariable(executionId, "order", null);
}
Defensive patterns

Strategy: validation

Validate before calling

if (value == null) {
    throw new IllegalArgumentException("Cannot store null as JPA entity variable");
}
runtimeService.setVariable(id, "order", value);

Type guard

boolean isNonNull(Object v) { return v != null; }

Try / catch

try {
    runtimeService.setVariable(id, "order", value);
} catch (ActivitiIllegalArgumentException e) {
    if (e.getMessage().contains("null value cannot be saved")) {
        runtimeService.setVariable(id, "order", null);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling getJPAClassString(null) directly, or storing a null variable through the JPA entity variable type mapping (e.g. taskService.setVariable / runtimeService.setVariable resolved to the JPA type) with a null value.

Common situations: A process variable set from an expression or delegate field that evaluates to null (entity not yet loaded/found); passing a result of em.find() that returned null; spring bean method returning null wired as a variable.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/variable/JPAEntityMappings.java:71

    }

    public EntityMetaData getEntityMetaData(Class<?> clazz) {
        EntityMetaData metaData = classMetaDatamap.get(clazz.getName());
        if (metaData == null) {
            // Class not present in meta-data map, create metaData for it and add
            metaData = scanClass(clazz);
            classMetaDatamap.put(clazz.getName(), metaData);
        }
        return metaData;
    }

    private EntityMetaData scanClass(Class<?> clazz) {
        return enitityScanner.scanClass(clazz);
    }

    public String getJPAClassString(Object value) {
        if (value == null) {
            throw new ActivitiIllegalArgumentException("null value cannot be saved");
        }

        EntityMetaData metaData = getEntityMetaData(value.getClass());
        if (!metaData.isJPAEntity()) {
            throw new ActivitiIllegalArgumentException("Object is not a JPA Entity: class='" + value.getClass() + "', " + value);
        }

        // Extract the class from the Entity instance
        return metaData.getEntityClass().getName();
    }

    public String getJPAIdString(Object value) {
        EntityMetaData metaData = getEntityMetaData(value.getClass());
        if (!metaData.isJPAEntity()) {
            throw new ActivitiIllegalArgumentException("Object is not a JPA Entity: class='" + value.getClass() + "', " + value);
        }
        Object idValue = getIdValue(value, metaData);
        return getIdString(idValue);

View on GitHub (pinned to d6d39ce1c6)