flowable/flowable-engine · error · FlowableException

no delete statement for ${entity.getClass()} in the ibatis m

Error message

no delete statement for ${entity.getClass()} in the ibatis mapping files

What it means

Thrown by DbSqlSession.flushDeleteEntities when the delete statement for the entity class resolves to null after statement mapping. No ibatis delete statement is registered for the entity type being deleted.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/DbSqlSession.java:682

        deletedObjects.clear();
        bulkDeleteOperations.clear();
    }

    protected void flushBulkDeletes(Class<? extends Entity> entityClass, List<BulkDeleteOperation> deleteOperations) {
        // Bulk deletes
        if (deleteOperations != null) {
            for (BulkDeleteOperation bulkDeleteOperation : deleteOperations) {
                bulkDeleteOperation.execute(sqlSession, entityClass);
            }
        }
    }

    protected void flushDeleteEntities(Class<? extends Entity> entityClass, Collection<Entity> entitiesToDelete) {
        for (Entity entity : entitiesToDelete) {
            String deleteStatement = dbSqlSessionFactory.getDeleteStatement(entity.getClass());
            deleteStatement = dbSqlSessionFactory.mapStatement(deleteStatement);
            if (deleteStatement == null) {
                throw new FlowableException("no delete statement for " + entity.getClass() + " in the ibatis mapping files");
            }

            // It only makes sense to check for optimistic locking exceptions
            // for objects that actually have a revision
            if (entity instanceof HasRevision) {
                int nrOfRowsDeleted = sqlSession.delete(deleteStatement, entity);
                if (nrOfRowsDeleted == 0) {
                    throw new FlowableOptimisticLockingException(entity + " was updated by another transaction concurrently");
                }
            } else {
                sqlSession.delete(deleteStatement, entity);
            }
        }
    }

    @Override
    public void close() {
        sqlSession.close();

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Add the delete statement to the entity's ibatis mapping XML.
  2. Register the delete statement name in DbSqlSessionFactory (setDeleteStatement) for the entity class.
  3. Verify the mapper XML is on the classpath and included in the MyBatis configuration.
  4. Compare against a built-in entity's mapping to ensure statement id and parameter conventions match.

Example fix

// before
// no delete in mapper -> null statement
// after
<delete id="deleteMyEntity" parameterType="...">DELETE FROM MY_ENTITY WHERE ID_ = #{id} AND REV_ = #{revision}</delete>
Defensive patterns

Strategy: retry

Validate before calling

long cnt = taskService.createTaskQuery().taskId(id).count();
if (cnt == 0) throw new IllegalStateException("Task gone: " + id);
// reload entity right before modification so revision is fresh
Task fresh = taskService.createTaskQuery().taskId(id).singleResult();
fresh.setName(name);

Try / catch

try { /* modify + save */ }
catch (FlowableOptimisticLockingException e) {
  // reload entity with fresh revision and retry the whole command (bounded retries)
}

Prevention

When it happens

Trigger: Deleting an entity (Entity.remove / persistence API) whose class has no delete statement name registered in DbSqlSessionFactory, or whose mapping XML lacks the delete statement.

Common situations: Custom entities with incomplete mapper XML (insert present, delete missing); mappings not deployed after an upgrade; class name changes breaking statement lookup.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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