hibernate/hibernate-orm · error · AuditException

Changeset does not exist: <changesetId>

Error message

Changeset does not exist: <changesetId>

What it means

AuditLog.getChangesetTimestamp(changesetId) selects the changelog's timestamp property filtered by the changeset-id property; getSingleResultOrNull() returning null means no changelog row exists for the given id, so AuditException('Changeset does not exist: <id>') is thrown. The value passed does not identify any committed audit changeset.

Source

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

			);
		}
	}

	// --- Changeset entity queries ---

	@Override
	public Instant getChangesetTimestamp(Object changesetId) {
		requireNonNull( changesetId, "Changeset identifier" );
		requireChangelog();
		final String hql = "select e." + timestampProperty
				+ " from " + changelogName + " e"
				+ " where e." + changesetIdProperty + " = :rev";
		final var result = auditSession
				.createSelectionQuery( hql, Object.class )
				.setParameter( "rev", changesetId )
				.getSingleResultOrNull();
		if ( result == null ) {
			throw new AuditException( "Changeset does not exist: " + changesetId );
		}
		return toInstant( result );
	}

	@Override
	public Object getChangesetId(Instant instant) {
		requireNonNull( instant, "Instant" );
		return resolveChangesetIdForTimestamp( resolveTimestampValue( instant ) );
	}

	@Override
	public <T> T findChangeset(Class<T> changelogClass, Object changesetId) {
		requireChangelog();
		final var result = auditSession.createSelectionQuery(
				"from " + changelogName + " where " + changesetIdProperty + " = :rev",
				changelogClass
		).setParameter( "rev", changesetId ).getSingleResultOrNull();
		if ( result == null ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Obtain changeset ids only from the audit API (AuditLog.getChangesets(entityClass, id), AuditEntry.changesetId()) instead of constructing them.
  2. Verify the argument's runtime type equals the changelog's changeset-id property type.
  3. If absence is expected, look up via findChangesets(changelogClass, Set.of(id)) first - it returns a map without throwing.

Example fix

// before - entity id passed where a changeset id is expected
Instant t = auditLog.getChangesetTimestamp(orderId);
// after
List<Object> csIds = auditLog.getChangesets(Order.class, orderId);
Instant t = auditLog.getChangesetTimestamp(csIds.get(0));
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm existence with the non-throwing bulk form first
Map<Object, RevEntity> found = auditLog.findChangesets(RevEntity.class, Set.of(csId));
if (found.isEmpty()) {
    // handle unknown changeset without an exception
}

Try / catch

try {
    Instant t = auditLog.getChangesetTimestamp(csId);
}
catch (AuditException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Changeset does not exist")) {
        // treat as not-found: verify the id came from getChangesets()/AuditEntry
    }
    else throw e;
}

Prevention

When it happens

Trigger: Passing an entity primary key instead of a changeset id, a changeset id of the wrong runtime type (long vs Long vs String), an id from another audit schema, or an id whose changeset transaction has not committed yet (the child changeset session was not flushed).

Common situations: Mixing up entity id and changeset id in API calls; type mismatch with the changelog's @Changelog.ChangesetId property type; concurrent readers querying before commit; rolled-back or deleted changesets.

Related errors


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