hibernate/hibernate-orm · error · AuditException
@Changelog.ChangesetId property '<changesetIdProperty>' is n
Error message
@Changelog.ChangesetId property '<changesetIdProperty>' is null after persisting changelog entity '<changelogClassName>'
What it means
ChangelogSupplier persists the changelog entity in a child session sharing the parent's connection, flushes, then reads the @Changelog.ChangesetId property (or the @Id when the property is the id). If the value is still null - the id was neither generated nor assigned - AuditException is thrown, because the changeset id is required to stamp every audit row of that changeset.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/audit/spi/ChangelogSupplier.java:189
* from the changelog entity after persistence.
* <p>
* Handles both regular properties and {@code @Id} properties.
*/
private Object readChangesetId(
Object changelog,
EntityPersister persister,
SharedSessionContractImplementor session) {
final Object changesetId;
final var changesetIdAttr = persister.findAttributeMapping( changesetIdProperty );
if ( changesetIdAttr != null ) {
changesetId = persister.getValue( changelog, changesetIdAttr.getStateArrayPosition() );
}
else {
// @ChangesetId is the @Id
changesetId = persister.getIdentifier( changelog, session );
}
if ( changesetId == null ) {
throw new AuditException(
"@Changelog.ChangesetId property '" + changesetIdProperty
+ "' is null after persisting changelog entity '"
+ changelogClass.getName() + "'"
);
}
return changesetId;
}
/**
* Persist the changelog entity using a child {@link Session}
* that shares the parent session's JDBC connection.
* The child session is returned so it can be kept open
* for deferred flush of {@code @ElementCollection} changes.
*/
private static Session persistChangelog(
SharedSessionContract session,
Object changelog) {
final var childSession = session.sessionWithOptions()View on GitHub (pinned to fad1729dce)
Solutions
- Annotate the changeset id with @GeneratedValue (sequence/identity) so it is populated on insert, or assign it yourself before persist.
- Prefer sequence-based or UUID/assigned ids so the id exists at insert time.
- If using identity generation, confirm the JDBC driver returns generated keys for the changelog table.
Example fix
// before - id never populated @Changelog.ChangesetId Long changesetId; // null after flush -> AuditException // after @Changelog.ChangesetId @GeneratedValue Long changesetId; // or set the value manually before persist
Defensive patterns
Strategy: validation
Validate before calling
// with assigned ids, populate the changeset id before flush
if (revEntity.getChangesetId() == null) {
revEntity.setChangesetId(nextId());
} Prevention
- Annotate the changeset-id property with @GeneratedValue (prefer sequences) or assign it in the constructor
- Verify the driver returns generated keys when using identity columns for the changelog table
- Cover changelog persistence in integration tests so id generation is exercised
When it happens
Trigger: A changelog entity whose changeset-id property is mapped without a generator and never set before persist; an identity/trigger generator whose value is not returned on insert; a generator whose assignment happens later than the post-flush read.
Common situations: Hand-built changelog entities with a plain Long field and no @GeneratedValue; drivers that do not support getGeneratedKeys for the table; custom @GenericGenerator strategies that defer id assignment.
Related errors
- Entity change tracking is not enabled. Use a @Changelog with
- No @Changelog configured. This operation requires a changelo
- Cannot convert changeset timestamp to Instant: <value>
- Audit graph mutation plan used with non-graph action queue
- Audit graph mutation plan used with non-graph action queue
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/e95565dc58ead5f3.
Report an issue: GitHub.