hibernate/hibernate-orm · error · IllegalArgumentException

Entity '<entityName>' is not audited

Error message

Entity '<entityName>' is not audited

What it means

Every per-entity AuditLog query (getChangesets, getModificationType, find..., getHistory) first resolves the entity descriptor and requires persister.getAuditMapping() != null. For an entity without auditing there are no audit tables to read, so IllegalArgumentException('Entity ... is not audited') is thrown before any query executes.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/audit/internal/AuditLogImpl.java:447

			throw new AuditException(
					"No @Changelog configured. "
							+ "This operation requires a changelog entity with "
							+ "@Changelog.ChangesetId and @Changelog.Timestamp fields."
			);
		}
	}

	@Override
	public void close() {
		if ( auditSession.isOpen() ) {
			auditSession.close();
		}
	}

	private String requireAuditedEntityName(Class<?> entityClass) {
		final var persister = sessionFactory.getMappingMetamodel().getEntityDescriptor( entityClass );
		if ( persister.getAuditMapping() == null ) {
			throw new IllegalArgumentException(
					"Entity '" + persister.getEntityName() + "' is not audited"
			);
		}
		return persister.getEntityName();
	}

	private static Instant toInstant(Object value) {
		if ( value instanceof Instant instant ) {
			return instant;
		}
		else if ( value instanceof LocalDateTime localDateTime ) {
			return localDateTime.atZone( ZoneId.systemDefault() ).toInstant();
		}
		else if ( value instanceof Date date ) {
			return date.toInstant();
		}
		else if ( value instanceof Long millis ) {
			return Instant.ofEpochMilli( millis );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Annotate the entity with @Audited so audit rows are written and it becomes queryable.
  2. If it is intentionally unaudited, guard the call with auditLog.isAudited(entityClass) and query the live entity instead.
  3. For inheritance hierarchies, ensure the concrete subclass you query is audited, not just the root.

Example fix

// before
List<Object> cs = auditLog.getChangesets(UnauditedThing.class, id);
// after
if (auditLog.isAudited(UnauditedThing.class)) {
    List<Object> cs = auditLog.getChangesets(UnauditedThing.class, id);
} else {
    // query the live entity instead
}
Defensive patterns

Strategy: validation

Validate before calling

// guard every audit call with the public check
if (auditLog.isAudited(entityClass)) {
    List<Object> csIds = auditLog.getChangesets(entityClass, id);
} else {
    // entity has no audit mapping: query the live table instead

Type guard

static boolean isAuditedEntity(AuditLog auditLog, Class<?> entityClass) {
    return auditLog.isAudited(entityClass);
}

Try / catch

try {
    List<Object> cs = auditLog.getChangesets(entityClass, id);
}
catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().endsWith("is not audited")) {
        // audit the entity or query the live table
    }
    else throw e;
}

Prevention

When it happens

Trigger: Passing a non-audited entity class to any AuditLog find/getChangesets/getModificationType method; passing a subclass that is excluded from auditing while only its root is audited (or vice versa).

Common situations: Mixed audited/unaudited domain models where audit queries are generic; refactoring that moved classes; intentionally unaudited entities reached through shared code paths.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/41adf9f0f8ff572f. Report an issue: GitHub.