hibernate/hibernate-orm · error · AnnotationException
Unable to interpret CacheMode in named query hint: " + query
Error message
Unable to interpret CacheMode in named query hint: " + queryName
What it means
QueryHintDefinition.getCacheMode reads the org.hibernate.cacheMode hint string and calls CacheMode.interpretExternalSetting, which only accepts the enum names NORMAL, IGNORE, GET, PUT, REFRESH (case-insensitive). An unrecognized value throws a MappingException that getCacheMode wraps in AnnotationException, naming the query. Validation happens at bootstrap while the named query definition is initialized.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/QueryHintDefinition.java:139
public Timeout getTimeoutRef() {
final Integer timeoutSeconds = getTimeout();
return timeoutSeconds == null ? null : Timeout.seconds( timeoutSeconds );
}
public boolean getCacheability() {
return getBoolean( HibernateHints.HINT_CACHEABLE );
}
@Nullable
public CacheMode getCacheMode() {
final String value = getString( HibernateHints.HINT_CACHE_MODE );
try {
return value == null
? null
: CacheMode.interpretExternalSetting( value );
}
catch (Exception e) {
throw new AnnotationException( "Unable to interpret CacheMode in named query hint: " + queryName, e );
}
}
@Nullable
public QueryFlushMode getFlushMode() {
final String value = getString( HibernateHints.HINT_FLUSH_MODE );
try {
return value == null
? null
: FlushModeTypeHelper.queryFlushModeFromHint( value );
}
catch (MappingException e) {
throw new AnnotationException( "Unable to interpret FlushMode in named query hint: " + queryName, e );
}
}
@Nullable
public LockMode getLockMode(String query) {View on GitHub (pinned to fad1729dce)
Solutions
- Use one of the five CacheMode names: NORMAL, IGNORE, GET, PUT, REFRESH (e.g. "GET" to read from cache without putting).
- If you meant to pick a collection/entity caching strategy, that belongs on @Cache(usage = ...) or region config, not the cacheMode hint.
- Remove the hint if you only wanted default NORMAL behavior.
Example fix
// before @QueryHint(name = "org.hibernate.cacheMode", value = "read-write") // after @QueryHint(name = "org.hibernate.cacheMode", value = "GET")
Defensive patterns
Strategy: validation
Validate before calling
@Test void cacheModeHintUsesEnumName() {
Set<String> valid = Set.of("NORMAL", "IGNORE", "GET", "PUT", "REFRESH");
for (QueryHint h : collectHints()) {
if ("org.hibernate.cacheMode".equals(h.name()))
assertTrue(valid.contains(h.value().toUpperCase(Locale.ROOT)),
"cacheMode must be one of " + valid + ", was: " + h.value());
}
} Type guard
boolean isValidCacheModeHint(String v) {
return Arrays.stream(CacheMode.values()).anyMatch(m -> m.name().equalsIgnoreCase(v));
} Try / catch
try {
metadata = sources.buildMetadata();
} catch (AnnotationException e) { // wraps CacheMode MappingException, names the query
failBuild("Unreadable cacheMode hint: " + e.getMessage());
} Prevention
- CacheMode is NORMAL/GET/PUT/REFRESH/IGNORE - concurrency strategies belong on @Cache(usage=...).
- Copy hint examples only from the Hibernate version you run; hint vocabularies drift across versions.
When it happens
Trigger: @QueryHint(name = "org.hibernate.cacheMode", value = "read-write") — a cache concurrency-strategy name rather than a CacheMode; also "use_query_cache", "ALL", or any made-up label. Fires when binding the named query that carries the hint.
Common situations: Confusing CacheMode (NORMAL/GET/PUT/REFRESH/IGNORE) with cache concurrency strategies (read-only/read-write/transactional); copying second-level-cache region config values into the cacheMode hint; values pasted from persistence.xml shared-cache-mode docs.
Related errors
- Named query hint [" + hintName + "] is not a boolean: " + qu
- Named query hint [" + hintName + "] is not an integer: " + q
- Named query definition is null
- Named query definition name is null: %s
- Duplicate named query '%s'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/5ba58271fcf4444b.
Report an issue: GitHub.